controller v0.119.0: storage-health coherence F8 + mapped_uid F4 — docs + CHANGELOG + REPORT + CONTEXT

F8: share row fuses the shipped v0.117 classifier (fuseNetHealth) so it agrees with
the stacks stub badge (one classification, two surfaces). F4: mapped_uid range check
1..65533 at the door. §3 fork = option B (controller-only). Live-validated on 9201.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017CDMFpFx84pfviCTVuGGhf
This commit is contained in:
2026-07-12 09:44:22 +02:00
parent 5d91fc8cce
commit a6da64da15
4 changed files with 98 additions and 45 deletions
+28
View File
@@ -1,5 +1,33 @@
## Changelog
### v0.119.0 — storage-health coherence (F8) + mapped_uid validation (F4) (2026-07-12) — MinAgent: 0.81.0
Fixes CAMPAIGN-3 (`felhom.eu/documentation/audits/CAMPAIGN-3-2026-07-11.md`) storage-UI findings.
MinAgent UNCHANGED (0.81.0) — controller-only; the §3 design fork took the recommended option **B**
(reuse the shipped v0.117.0 classifier), so no agent change.
- **F8 (MED) — one classification, two surfaces.** The share row's health used to come only from the
agent's SERVER-LEVEL TCP dial (`server:2049/445`), which stays green when a *single* export is
`exportfs -u`'d — so the row showed benign "Készenlét" while the stacks/dashboard already showed the
stub reality. `networkStorageItems` now FUSES the agent view with the consuming-namespace
classification (`fuseNetHealth` → the same `system.ClassifyPathFS` the stacks stub badge reads): a new
`stub` health state wins over a benign idle/ok when the namespace sees local disk at `Where`; a
whole-server `unreachable` still wins over stub; autofs-healthy / network / inconclusive `unknown`
leave the agent health untouched (never manufacture a fault, never force-mount an idle trigger). The
row badge for `stub` = "Hibás — az alkalmazások nem a NAS-t látják". The row and the stacks/dashboard
badge now derive from ONE classification and can never contradict.
- **F4 (LOW) — mapped_uid/gid validated at the door.** `handleNetStorageAdd` range-checks the container
uid/gid (1..65533) after the `<=0` default, BEFORE the job starts. Out of range → an immediate,
friendly Hungarian 400 ("Az alkalmazás felhasználói azonosítója (uid) érvénytelen…"), nothing
installed — the campaign's `mapped_uid:101000` (a host-side mapped value) previously slipped past the
controller and failed only at the agent with a raw `agent_error`.
- **Live-validated (demo 9201):** F8 — `exportfs -u` while idle + drop-mount → the share row flipped to
`stub`/"Hibás — az alkalmazások nem a NAS-t látják" AND the stacks stub badge showed (4), the two
surfaces AGREE; re-export → row cleared to `ok`/"Elérhető" (healthy idle NOT downgraded). F4 —
`mapped_uid:101000` → 400 + friendly message, registry unchanged; `mapped_uid:1000` passed the range
check. Tests incl. the F8 fusion companion (revert → row idle → fail), the autofs-not-stub guard, and
the F4 boundary (65533 pass / 65534 fail), all green.
### v0.118.0 — backup integrity: atomic volume dumps (F7) + no single-copy (F6) + stale-primary sweep (F5) (2026-07-12) — MinAgent: 0.81.0
Fixes CAMPAIGN-3 (`felhom.eu/documentation/audits/CAMPAIGN-3-2026-07-11.md`) backup findings. MinAgent
+14 -1
View File
@@ -7,7 +7,20 @@
>
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
Last updated: 2026-07-12 (v0.118.0 — backup integrity F7/F6/F5; MinAgent 0.81.0 unchanged)
Last updated: 2026-07-12 (v0.119.0 — storage-health coherence F8 + mapped_uid F4; MinAgent 0.81.0 unchanged)
> **2026-07-12 — v0.119.0: STORAGE-HEALTH COHERENCE (LIVE on 9201). MinAgent 0.81.0 unchanged.** Fixes
> CAMPAIGN-3 F8+F4. **F8 (MED):** the share row's health came only from the agent's server-level TCP
> dial (blind to a single unexported share) → it showed benign "Készenlét" while the stacks cards
> showed the stub — a contradictory UI. `networkStorageItems`→`fuseNetHealth` now reuses the SAME
> `system.ClassifyPathFS` the stacks stub badge reads (§3 fork = option B, controller-only): a new
> `stub` health (badge "Hibás — az alkalmazások nem a NAS-t látják") overrides idle/ok when the
> namespace sees local disk; `unreachable` still wins over stub; autofs/network/unknown leave agent
> health intact (never force-mount). Row + stacks badge now share ONE classifier → can't contradict.
> **F4 (LOW):** `handleNetStorageAdd` range-checks container uid/gid 1..65533 (`validMappedID`) →
> friendly 400, nothing installed (was raw agent_error on 101000). LIVE: F8 row=stub matching stacks
> badge through an exportfs cut, cleared to ok on re-export; F4 uid 101000→400, uid 1000 passes.
> Seam: `s.classifyFSPath`. Task D (fix-3 alerting + ring revision) still queued.
> **2026-07-12 — v0.118.0: BACKUP INTEGRITY (LIVE on 9201). MinAgent 0.81.0 unchanged.** Fixes
> CAMPAIGN-3 backup findings (`documentation/audits/CAMPAIGN-3-2026-07-11.md`). **F7 (HIGH) atomic
+43 -44
View File
@@ -1,60 +1,59 @@
# REPORT — v0.118.0: backup integrity (F7 atomic dumps · F6 no-single-copy · F5 stale sweep)
# REPORT — v0.119.0: storage-health coherence (F8) + mapped_uid validation (F4)
**Date:** 2026-07-12 · **Version:** controller v0.118.0 (from v0.117.0) · **MinAgent:** 0.81.0 (UNCHANGED)
· **Deployed:** guest 9201 (`0.118.0` healthy) · **Source:** `felhom.eu/documentation/audits/CAMPAIGN-3-2026-07-11.md`.
**Date:** 2026-07-12 · **Version:** controller v0.119.0 (from v0.118.0) · **MinAgent:** 0.81.0 (UNCHANGED)
· **Deployed:** guest 9201 (`0.119.0` healthy) · **Source:** `felhom.eu/documentation/audits/CAMPAIGN-3-2026-07-11.md`.
## §3 design fork — decision
Took the recommended **option B (controller-only)**: the share row reuses the shipped v0.117.0
consuming-namespace classifier (`system.ClassifyPathFS`) — the exact ground truth the stacks-page stub
badge already reads. No agent change, no new probing surface, and by construction the row and the
stacks badge can never disagree (single source). Option A (agent-side export-level probe) was not built.
## What shipped
- **F7 (HIGH) — atomic volume dumps.** `backup.DumpAppVolumes` writes the tar to `<vol>.tar.tmp`,
fsyncs it, then atomically `os.Rename`s over the restore point ONLY on success (+ best-effort dir
fsync via `atomicPromoteTar`) — mirroring the DB-dump path (`dbdump.go` DumpOne). Any tar
error / timeout / dead-NFS EIO removes only the `.tmp`; the last good `.tar` is byte-untouched. The
`.tar.tmp` is invisible to restore-point/stale scans (ends `.tmp`, not `.tar`); orphan tmps are
swept. New `tarVolume` seam for the red-proof.
- **F6 (LOW) — no single-copy.** `RunAllTier2` no longer skips non-HDD apps → volume-only apps get a
cross-drive tier-2 copy. sys_drive restore-point label is now clear ("Belső SSD (rendszer)"), never
blank. `SingleCopyWarning` (honest HU banner) shows on a single-drive box with no off-drive target.
- **F5 (LOW) — stale primary sweep.** `pruneStalePrimaryDirs` (end of each backup cycle) removes an
orphaned `backups/primary/<app>` dir on an OLD drive after an HDD_PATH move. Guards: deployed +
different-current-drive only; never the current-drive dir or an undeployed app's dir; strictly under
a `backups/primary/` prefix.
- **Part 4 — locality fork: operator chose (A) keep locality, document-only.** No code change; the
NAS-outage behaviour + tier-2 mitigation are documented plainly.
- **F8 (MED) — one classification, two surfaces.** `networkStorageItems` now fuses the agent's health
with the namespace classification via `fuseNetHealth`: a new `stub` state overrides a benign idle/ok
when the consuming namespace sees local disk at `Where`; a whole-server `unreachable` still wins over
stub; autofs-healthy / network / `unknown` leave the agent health intact (no manufactured fault, no
force-mount). Row badge for `stub` = "Hibás — az alkalmazások nem a NAS-t látják". The share row and
the stacks/dashboard badge now derive from ONE classifier.
- **F4 (LOW) — mapped_uid/gid range check at the door.** `handleNetStorageAdd` validates the container
uid/gid (1..65533) after the `<=0` default, before the job — out of range → friendly Hungarian 400,
nothing installed. Catches the campaign's `101000` (a host-side mapped value) that used to leak a raw
`agent_error`.
## Tests + red-proofs (all green)
- F7: happy atomic promote (no `.tmp` left); **truncation red-proof** — pre-seeded good `.tar`, tar
fails mid-write → original byte-unchanged, no 0-byte (revert to in-place write → fails); leftover
`.tar.tmp` swept.
- F6: volume-only app in the tier-2 run set (revert the skip → fails); single-drive honest signal;
sys_drive drive-label non-blank.
- F5: redeployed residue removed; **guard red-proofs** — undeployed app's restore point kept,
current-drive dir kept.
- Existing `TestListRestorePoints_DriveLabel/SSD` updated (blank → clear label, the F6 behaviour).
- F8 fusion table: idle+stub→stub (the contradiction resolved), ok+stub→stub, **idle+autofs→idle**
(the over-eager autofs=stub mutant fails here), unreachable+stub→unreachable (server wins),
idle+unknown→idle (no manufactured fault). End-to-end `networkStorageItems` stub fusion (companion:
drop the fuse call → row shows raw agent health → fail).
- F4: uid 101000 → 400 + friendly message, agent never reached (companion: drop the check → reaches the
agent → fail); 65534 → 400; 1000 / 65533 / 0-defaults pass the range check.
## Live validation (demo 9201, real pipeline, sim-NAS rails — exportfs only)
## Live validation (demo 9201, sim-NAS rails — exportfs only)
- **F7 money-shot:** established good tars (radarr sha `847a97ad…` +4 others), then `exportfs -u` the
NFS target 4 s into a backup during volume dumps → run `success:false`; **all 5 nas-media volume
tars BYTE-IDENTICAL** (clean host-side diff), **no 0-byte file, no leftover `.tar.tmp`**; the next
clean run produced fresh good tars (`success:true`). This is the exact campaign F7 repro, now safe.
- **F6:** after a tier-2 cycle, volume-only `actualbudget` + `seerr` appear under
`felhom-usb/backups/secondary/` (previously none); `actualbudget` snapshot `drive_label` =
"Belső SSD (rendszer)"; single-copy banner correctly absent on the multi-drive demo.
- **F5:** a seeded stale `nas-media/backups/primary/actualbudget` (actualbudget lives on sys_drive) was
swept by the next backup; the real sys_drive dir + radarr's nas-media dir were kept.
- **Restore round-trip:** calibre-web tamper → restore → BYTE-IDENTICAL (53 files), container healthy —
F7's write change did not affect restore.
- **F8 the contradiction, killed:** baseline healthy → row `ok`, no stub badge. `exportfs -u` while idle
+ drop the mount → the SHARE ROW showed `health=stub` ("Hibás — az alkalmazások nem a NAS-t látják")
AND the stacks page showed the stub badge (4) — the two surfaces AGREE (previously: row "Készenlét" +
stacks stub = contradiction). `reachable:true` throughout (the server-level dial is still green — the
exact F8 blindness, now correctly overridden). Re-export → row cleared back to `ok`/"Elérhető"
(healthy idle NOT downgraded — the regression).
- **F4:** `mapped_uid:101000` → 400 + the friendly message, registry unchanged (no `c5uid`), no host
unit/dir residue; `mapped_uid:1000` → 200, passed the range check (then failed later at the
unreachable probe as designed, rolled back clean).
## NOT live-validated / standing items
- Single-drive `SingleCopyWarning` live (the demo has enrolled drives) — unit-tested only.
- Task C (F8/F4) and Task D (ring/alerting) remain queued.
- Peti's box (controller 0.113 / agentless-on-proxmox2) reaches 0.118 + agent 0.85 at his next train —
noted for the roadmap; the agentless-on-proxmox2 cluster gap stands.
- `unreachable`-wins live (a genuine server-down IP on a registered share) — unit-tested only; the F8
live proof used the export-level cut (the actual finding).
- Task D remains queued: fix-3 boot-time app-start-failure alerting + the ring wrap/count revision
(fix-6 6.5-min horizon under load).
- Peti's box (controller 0.113 / agentless-on-proxmox2) reaches 0.119 (+0.85/0.118) at his next train.
- No publish/floor movement; agent untouched (MinAgent 0.81.0).
## Box state at wrap
controller 0.118.0 healthy on 9201; all backups consistent (fresh tier-1 + tier-2 incl. volume-only
apps); NAS re-exported; no stale test residue.
controller 0.119.0 healthy on 9201; nas-media healthy + `ok`; registry = nas-media only (no test
residue); all NAS apps healthy; NFS re-exported.
+13
View File
@@ -864,6 +864,19 @@ not just those with HDD data. Non-HDD apps can configure destination, method, an
> alkalmazás nem a NAS-t látja", `NetworkStubs`) that WINS over the recoverable `unreachable` badge.
> The agent pair (v0.84.0 `ReassertNetworkMounts`) re-arms triggers on guest start; this layer is the
> detection net beneath it.
> - **One classification, two surfaces (v0.119.0, CAMPAIGN-3 F8):** the **share row** on the
> `/storage/network` page now reads the SAME classifier. Its health used to come only from the agent's
> server-level TCP dial (`server:2049/445`), which stays green when a *single* export is `exportfs -u`'d
> — so the row showed benign "Készenlét" while the stacks cards showed the stub. `fuseNetHealth`
> (`netstorage_handlers.go`) fuses the agent view with `classifyFSPath(Where)`: a new **`stub`** health
> (badge "Hibás — az alkalmazások nem a NAS-t látják") overrides idle/ok when the namespace sees local
> disk; a whole-server `unreachable` still wins over stub; autofs-healthy / network / `unknown` leave
> the agent health intact (never force-mount an idle trigger). The row and the stacks badge derive from
> ONE classifier and can never contradict.
> - **mapped_uid validated at the door (v0.119.0, F8's sibling F4):** `handleNetStorageAdd` range-checks
> the container uid/gid (**1..65533**; the guest maps `<uid>`→`<uid>+100000`, so 65534=nobody and a
> host-side value like 101000 must not be entered) after the `<=0` default — out of range → a friendly
> Hungarian 400, nothing installed (previously a raw `agent_error` from the agent).
> - **Deploy view truth (v0.117.0, RCA fix 4):** a deployed app's read-only storage select now marks
> `selected` by the app's STORED `HDD_PATH` (extra disabled `<path> (nem elérhető)` option when the
> stored path left the schedulable list) — `IsDefault` selects only for NEW deploys. Pre-fix the view