Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017CDMFpFx84pfviCTVuGGhf
6.4 KiB
REPORT — agent v0.85.0: the boot/recovery plane + appliance self-heal (CAMPAIGN-3 Task A)
Date: 2026-07-12 · Baselines: agent v0.84.0 (bec4bac) → v0.85.0; felhom.eu post-f97f7803.
Source of truth: felhom.eu/documentation/audits/CAMPAIGN-3-2026-07-11.md.
Scope: F12 (CRITICAL), F11/F10/F9 (HIGH/CRITICAL), F2/F1 (LOW), the guest-hook rc255 belt, and the
operator-mandated appliance-mode node self-heal. Controller untouched (MinAgent unchanged). Green gate
(go build ./... && go vet ./... && go test ./...) PASS on the build server.
What shipped
- F12 (CRITICAL) — boot ordering cycle. Both rendered units drop
After=/Wants=network-online.target(.mountkeeps_netdev;.automountgets no network relation).MigrateNetworkUnits— a general template-drift reconcile (SHA-256 content compare vs a fresh render of the reconstructed spec; rewrite + one batcheddaemon-reload; idempotent) — repairs ALREADY-INSTALLED units at startup and at the head ofEnsureNetworkMount. - F11 — read the right unit (
netReassertClassify): decision by host/proc/mountsfstype only, never the automount's own state. Matrix-corrected: a rebooted guest inherits neither an idle trigger NOR an active mount, so the callers re-arm reactively when a running guest is found blind (RearmNetworkAutomountafter a failed visibility check). - F10 — re-arm for real: a failed/start-limited unit is
reset-failedbeforeenable --now(unprivilegedsystemctl is-failedseam + new sudoers verb). - F9 — say what you did: enumerate by marker-owned unit files; one INFO verdict per share.
- F2/F1 — zero residue:
RemoveNetworkMount(and every verify-fail rollback via it) reset-failed's the pair andrmdirs the mountpoint (rmdir-only fail-safe; neverrm -rf). - Hook can never take a guest down:
runHookPhase— per-phase recover + hard timeout, always returns; the installed wrapper snippet no longerexecs (… || true; exit 0). - Appliance self-heal (
internal/selfheal): one heal — host networking recovery, gated ondeployment_mode:"appliance"; byo runs the check + WARNs, remedy unreachable. Configdeployment_mode(+FELHOM_AGENT_DEPLOYMENT_MODE,IsAppliance()fail-safe-to-byo).
Sudoers — three grants added (LOUD, per the no-widening rule)
Shipped with the binary; capability manifest updated (probed live: 62/62, 0 degraded):
systemctl reset-failed -- mnt-felhom*(FELHOM_NETMOUNT) — F10: no platform path could re-arm a start-limited automount. reset-failed only clears a failed latch; it cannot start/stop anything.rmdir /mnt/felhom-drives/*(FELHOM_NETMOUNT) — F1: removed-share mountpoint cleanup. rmdir refuses a non-empty dir (fail-safe; unexpected data is preserved, neverrm -rf'd).systemctl start networking.service(new FELHOM_SELFHEAL alias) — F12-class appliance self-heal. Fixed unit, no glob; the grant alone cannot harm; the remedy is ALSO code-gated on appliance mode.
Tests + red-proofs (all green)
- Render: no network-online in either unit,
_netdevpresent (companion: re-adding the line fails). - Reconcile: drifted (legacy) unit rewritten once + batched daemon-reload; idempotent (0 rewrites 2nd pass); spec round-trip exact; foreign units ignored.
- Reassert: fstype table with the automount-state-ignored red-proof; reset-failed+rearm verdict; per-unit verdict count (F9); re-arm-on-guest-blind (F11 matrix-correction); byo-blind non-fatal.
- Hook rc-0 under panic + timeout (Part 4); zero-residue (reset-failed + rmdir, never rm -rf).
- Self-heal state machine; byo-never-invokes red-proof (byo + unhealthy → zero privileged invocations); absent field ⇒ byo.
Live MATRIX (demo felhom-pve, agent 0.85.0 + deployment_mode=appliance)
Deploy verified: startup migrated the installed nas-media units (0 network-online lines); no interim drop-in existed to remove (the campaign recovery used reset-failed+restart, not a drop-in).
| # | Row | Result |
|---|---|---|
| 1 | guest reboot × idle share ×2 | PASS — zero-touch: reassert re-armed, guest sees NAS, write-through OK, 4 NAS apps auto-started |
| 2 | guest reboot × active share | F13 (new finding) — active nfs4 under mp8 can fail PVE's rbind (rc255, idmap); NAS-down-at-boot → apps Created (deferred fix-3). Independent of the felhom hook (rc0). |
| 3 | F10 manufacture → agent restart | PASS — sweep reset-failed both units (loud WARN) + re-armed (verdict reset-failed+rearmed); the campaign's silent no-op is gone |
| 4 | F10 state → guest reboot (hook path) | PASS — guest STARTS (rc0); F11 matrix-correction healed the blind guest live ("re-arming … visible after re-arm (healed)") |
| 5 | 🛑 host reboot ×2 | PASS — grep -c "ordering cycle" == 0 on BOTH boots, networking up, automount armed, guest + 8 apps healthy. F12 coin flip is dead. |
| 6 | 🛑 stop networking (appliance) | PASS — watchdog WARNed active=false, fired the privileged start, recovered on attempt 1 (~11 s). BYO live variant NOT run (red-proof only). |
| 7 | refused add + remove cycles ×3 | PASS — zero residue (0 units/dirs/failed/creds; campaign left 10 dirs); registry clean |
NOT live-validated / standing items
- Row 6 byo variant — not run live (would leave the box dark); byo is red-proof-tested only.
- F13 (NEW, HIGH) — active-nfs-under-mp8 rc255 + NAS-down-at-boot apps-
Created. Deferred (needs a pre-start idle-unmount design for multi-guest hosts, or an idmapped nfs mount + fix-3 alerting). - Peti runs agent 0.81 — unaffected by this train until his rollout; if his box has a NAS share he carries the F12 coin flip until then (interim drop-in recipe below).
- Task B/C/D findings (F7/F6/F5, F8/F4, ring/alerting) remain queued.
- Publish train: 0.85 is customer-critical (F12 affects any customer with a NAS share) — prioritize.
Box state at wrap
agent 0.85.0 / deployment_mode=appliance; nas-media clean (no ordering cycle) + active; 0 failed felhom units; 0 leftover mountpoint dirs (19 pre-fix campaign leftovers rmdir'd); guest 9201 + all 10 containers healthy with NAS data intact.
Interim F12 mitigation for a not-yet-upgraded host
A systemd drop-in on each installed mnt-felhom…-<name>.automount with empty After=/Wants=
overrides + daemon-reload strips the ordering until the 0.85 agent migrates the unit at startup.