diff --git a/documentation/controller/network-storage-nas.md b/documentation/controller/network-storage-nas.md index cf36bd4..dc4dae2 100644 --- a/documentation/controller/network-storage-nas.md +++ b/documentation/controller/network-storage-nas.md @@ -110,13 +110,42 @@ with a hint; the rollback still runs. ## Health model -Per-share liveness only (`ok | idle | unreachable`): `idle` (automount idle-unmounted) is the +Per-share liveness (`ok | idle | unreachable | stub`): `idle` (automount idle-unmounted) is the benign steady state; `unreachable` degrades the affected share's apps ONLY — never box health, never the drive missing→stop cascade (kind-gated). Timing/budget note for integrators: the verify worst case is systemd's 90 s (black-holed server) — verify traffic therefore rides the detached job + status poll, never a single long HTTP call (the agentapi client keeps its global 15 s timeout). +### One classification, two surfaces (F8, controller v0.119.0, CAMPAIGN-3) + +The agent's per-share `health` derives from a **server-level TCP dial** (`server:2049/445`). That dial +stays GREEN when a *single* export is `exportfs -u`'d — the server still answers on 2049 for its other +exports — so the agent reports benign `idle`+`reachable:true` for a share whose consuming namespace is +actually a stub. The campaign found the resulting **contradictory UI**: the share row showed +"Készenlét" while the stacks/dashboard cards showed the stub badge (which reads the consuming-namespace +classifier shipped in v0.117.0). + +The fix (controller-only — the §3 fork's recommended option B): the share row (`networkStorageItems` → +`fuseNetHealth`) reads the SAME `system.ClassifyPathFS` the stacks stub badge reads and fuses it over +the agent view. Precedence: a whole-server `unreachable` (agent dial failed) is the most actionable and +WINS; otherwise a **`stub`** classification at `Where` (namespace sees local disk, not the NAS) +overrides a benign idle/ok — the row badge becomes "Hibás — az alkalmazások nem a NAS-t látják"; an +autofs-healthy trigger, a real network fs, or an inconclusive `unknown` read leave the agent-derived +health untouched (**never force-mount an idle trigger** — that is healthy by June's idle-unmount +design). The share row and the stacks badge now derive from ONE classification and can never +contradict. **Live-proven 2026-07-12:** an `exportfs -u` while idle flipped the row to `stub` in lockstep +with the stacks badge; re-export cleared both to `idle`/`ok`, with `reachable:true` throughout (the +agent's server-level view unchanged — the fusion is what carries the export-level truth). + +### Input validation — mapped_uid range (F4, v0.119.0) + +The add handler range-checks the container uid/gid (**1..65533**) at the door, after the `<=0` default. +The guest maps `` to `+100000` on the host, so 65534 is `nobody` and a host-side mapped value +(e.g. 101000 = 1000+100000) must never be entered as the app uid. Out of range → a friendly Hungarian +400, nothing installed — the campaign's `mapped_uid:101000` previously slipped past the controller and +failed only at the agent with a raw `agent_error`. + ## Synology (DSM) — validated recipe (SPIKE-nas-dsm-2026-07-11, real DSM 7.2) The consumer recipes were validated end-to-end against a real DSM 7.2 (virtual-dsm) through the live