v0.81.0: NAS verify-before-commit — retry=0, journal classifier, detached verify job + auto-rollback

Agent half of the verify-before-commit task (SPIKE-nas-verify-2026-07-11, b57f6c1):
retry=0 in the production NFS options (Q4-vi); ClassifyNetVerifyFailure on the live
Q4 strings (nfs_export merges not-found/not-permitted); add = sync fast-fail (2s TCP
pre-probe, nothing installed) + detached in-memory verify job judging /proc/mounts
only, auto-rollback on failure; GET /netstorage/verify-status (phase none = the
controller's Scenario-F rollback signal); unprivileged journalctl (systemd-journal
group, NO new sudoers grants).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-11 09:44:06 +02:00
parent 300f06722b
commit added9d226
11 changed files with 859 additions and 26 deletions
+33
View File
@@ -1,3 +1,36 @@
## v0.81.0 — NAS verify-before-commit: retry=0 + detached verify job + journal classification (2026-07-11)
Implements the agent half of the "NAS verify-before-commit" task on the SPIKE-nas-verify-2026-07-11
evidence (felhom.eu/documentation/audits/, commit b57f6c1). `POST /netstorage/add` no longer succeeds
blind — a bogus share can no longer sit at "Készenlét" forever.
- **`retry=0` in the production NFS option string** (`netmount.go mountOptions`; Q4-vi): a dead-NAS
on-demand access now fails clean in ~4 s (ENODEV) instead of wedging the accessing app until
systemd's 90 s start cap; verify failures classify as "No route to host" instead of a
diagnostic-free timeout. SMB string unchanged (retry is a mount.nfs option). Already-installed
units are NOT rewritten (pre-customer; re-add re-creates them).
- **`internal/storage/netverify.go`** — `ClassifyNetVerifyFailure`: pure, table-driven journal
classification on the Q4 VERBATIM substrings → `unreachable | nfs_export | smb_auth | smb_share |
timeout | mount_failed`. `nfs_export` deliberately MERGES not-found/not-permitted (NFSv4 returns
the identical string for both — Q4 ii≡iii). Everything exits rc=32, so classification is
string-based by design.
- **Detached verify job** (`localapi/netverifyjob.go`, the formatjob shape but IN-MEMORY single
slot): add = decode → role-gate → SYNC fast-fail (full spec validation + 2 s TCP pre-probe; an
unreachable server is refused with NOTHING installed) → stage SMB creds → EnsureNetworkMount →
detached verify (trigger read through the automount; mount success judged from **/proc/mounts
only** — never readability, a 0700 export EACCES is a good mount) → on failure: journal-classify
+ **auto-rollback** (RemoveNetworkMount + creds file). `GET /netstorage/verify-status` reports the
slot; phase `none` after an agent restart is the controller's rollback signal (Scenario F —
deliberately not persisted). Single-flight: a second add while verifying is a 409.
- **Journal access is UNPRIVILEGED** (`journalctl -u <unit> -n 20 -o cat`, no sudo, no new sudoers
grant): requires the felhom-agent user in the `systemd-journal` group (host-install ≥ v1.12.0
successor adds it; existing hosts: `usermod -aG systemd-journal felhom-agent`). Journal
unavailable degrades to `mount_failed` + a hint, still rolled back.
- New exported helpers: `storage.NetworkMountedAt` (autofs trigger ≠ mounted),
`storage.NetworkEndpointReachable` (the 2 s pre-probe). REUSE.md updated.
- Tests: classifier table on the live Q4 strings, §8 truth table, rollback effects, pre-probe
zero-install, single-flight + no-job shape. Red-proof outcomes recorded in REPORT.md.
## v0.80.0 — PBS DR tier SLICE 2: the apply-bridge (2026-07-10)
Consumes hub v0.44.0's `pbs_dr` desired-state descriptor (slice 1): hub tick → the box grows the