docs: REPORT v0.82.0 (version channel deployed) + REUSE version-channel seam
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -1,58 +1,38 @@
|
|||||||
# REPORT — agent v0.81.0: NAS verify-before-commit (retry=0 + classifier + detached verify job) — DEPLOYED
|
# REPORT — agent v0.82.0: local-API version channel (X-Felhom-Agent-Version) — DEPLOYED
|
||||||
|
|
||||||
**Date:** 2026-07-11 · **Class:** implementation (agent half of the cross-repo "NAS
|
**Date:** 2026-07-11 · **Class:** implementation (agent slice of the NAS/coupling backlog-clear
|
||||||
verify-before-commit + page redesign" task). Baseline v0.80.0 (`300f06722b`) → **v0.81.0**
|
task, Part B). Baseline v0.81.0 (`1e60e88`) → **v0.82.0** (`fa9c7fe`), live on felhom-pve.
|
||||||
(`added9d`), live on felhom-pve. Pairs with controller v0.113.0 + host-install v1.13.0.
|
Pairs with controller v0.115.0 (version-aware Supports) + hub v0.45.0.
|
||||||
**Full task-wide record (red-proof outcomes, live A–E transcripts, teardown):**
|
|
||||||
`felhom-controller/REPORT.md`. Evidence base: SPIKE-nas-verify-2026-07-11 (b57f6c1).
|
|
||||||
|
|
||||||
## What shipped (agent)
|
## What shipped
|
||||||
|
|
||||||
- **`retry=0` in the production NFS option string** (`netmount.go mountOptions`, Q4-vi): a dead-NAS
|
- **`X-Felhom-Agent-Version` on EVERY local-API response** (`localapi/server.go`: the mux is wrapped
|
||||||
on-demand access fails clean in ~3.8 s (ENODEV) instead of a 91 s wedge; verify failures classify
|
so the header is set on all routes + all statuses, INCLUDING auth failures and 404s — the
|
||||||
as `No route to host` instead of a diagnostic-free systemd timeout. SMB unchanged (mount.cifs
|
controller learns the agent version passively from ordinary traffic). Value = `main.version` via
|
||||||
rejects `retry=`). Installed units are NOT rewritten (none existed on the demo anyway).
|
the new `localapi.Options.AgentVersion`. No new routes, no envelope changes, **no sudoers changes**.
|
||||||
- **`internal/storage/netverify.go`** — `ClassifyNetVerifyFailure(journalTail, tcpReachable)`:
|
- The controller (v0.115.0) reads it and capability-gates by version comparison; header-less
|
||||||
pure, first-match table on the Q4 VERBATIM substrings → `unreachable | nfs_export | smb_auth |
|
(≤0.81.0) agents keep working — the controller falls back to the v0.114.0 route probe unchanged.
|
||||||
smb_share | timeout | mount_failed`; `nfs_export` deliberately merges not-found/not-permitted
|
|
||||||
(NFSv4 identical strings). String-based BY DESIGN — every mount failure exits rc=32.
|
|
||||||
- **Detached verify job** (`localapi/netverifyjob.go`, formatjob shape but IN-MEMORY single slot —
|
|
||||||
a restart empties the slot; `GET /netstorage/verify-status` phase `none` is the CONTROLLER's
|
|
||||||
rollback signal, Scenario F): add = decode → role-gate → **sync fast-fail** (full spec validation
|
|
||||||
+ 2 s TCP pre-probe; unreachable = HTTP 502 `{code:unreachable}`, NOTHING installed) → SMB creds
|
|
||||||
(0600) → EnsureNetworkMount → detached verify: trigger read through the automount, success judged
|
|
||||||
from **/proc/mounts ONLY** (§8: EACCES on a 0700 export is a GOOD mount), failure = journal
|
|
||||||
classification + **auto-rollback** (RemoveNetworkMount + creds). Single-flight: second add = 409.
|
|
||||||
- **Unprivileged journal read** (`journalctl -u <unit> -n 20 -o cat`, NO sudo, NO new sudoers
|
|
||||||
grant): needs the `systemd-journal` group (host-install v1.13.0; existing hosts:
|
|
||||||
`usermod -aG systemd-journal felhom-agent`). Unavailable ⇒ degrade to `mount_failed` + hint,
|
|
||||||
still rolled back.
|
|
||||||
- New exports: `storage.NetworkMountedAt`, `storage.NetworkEndpointReachable`. REUSE.md updated
|
|
||||||
(helpers + the in-memory-vs-persisted job pattern row + the four Server net-verify seams).
|
|
||||||
|
|
||||||
## Tests + red-proofs (agent side; outcomes verbatim in felhom-controller/REPORT.md)
|
## Tests + red-proof
|
||||||
|
|
||||||
A1 retry=0 (revert → fail) · A2 classifier table incl. merged nfs_export + empty-journal
|
`TestVersionHeader_OnEveryResponse` (authed / unauthed / 404 all carry the header) +
|
||||||
degradation (exit-code mutant → every non-generic row fails) · A3 rollback effects via fakes
|
`TestVersionHeader_OmittedWhenUnset`. Red-proof: dropping the Handler() wrap (return the bare mux) →
|
||||||
(drop rollback → `removed=[]` + creds linger) · A4 §8 truth table (readability mutant → both rows
|
every row fails with an empty header (observed: `X-Felhom-Agent-Version = "", want "9.9.9-test"`),
|
||||||
fail) · A5 pre-probe zero-install (skip probe → 200-with-install) · A6 single-flight + no-job shape
|
reverted → green. Full `go build && go vet && go test ./...` PASS (the known
|
||||||
(drop check → 409 lost). Green gate `go build && go vet && go test ./...` PASS (known
|
TestGenerateRecoveryCode wordlist flake did not recur this run).
|
||||||
TestGenerateRecoveryCode wordlist flake hit once, clean on re-run).
|
|
||||||
|
|
||||||
## Deployed + verified (felhom-pve)
|
## Deployed + verified
|
||||||
|
|
||||||
`.bak-0.80.0` kept → `install -m0755` → `usermod -aG systemd-journal felhom-agent` →
|
felhom-pve: `.bak-0.81.0` kept → `install -m0755` → restart; `felhom-agent --version` → 0.82.0;
|
||||||
`systemctl restart felhom-agent`. Verified: `felhom-agent 0.81.0`; `id felhom-agent` →
|
clean start journal (enrolled drive re-bound, local-api listening). Wire proof: an authed
|
||||||
`…,999(systemd-journal)`; clean start journal (enrolled drive re-bound, local-api listening,
|
`GET /storage` to the live agent returns `x-felhom-agent-version: 0.82.0`. **NOT published** —
|
||||||
desired-state gen 10). Live proof the journal path works: Scenario A's failure detail carried the
|
demo-deploy only; Peti stays 0.81.0 (the live probe-fallback path). Hub now shows demo agent 0.82.0,
|
||||||
REAL `mount.nfs4 … reason given by server: No such file or directory` lines read unprivileged.
|
Peti 0.81.0.
|
||||||
|
|
||||||
**NOT published:** 0.81.0 is deployed on the demo only — no Gitea publish, Day-0 manifest untouched
|
## Observations (documented, NOT acted on)
|
||||||
(the publish train is the operator's separate decision); Peti's box untouched.
|
|
||||||
|
|
||||||
## Observations
|
- **Q1c FAIL surfaced during this task's Part E** (supervised, agent 0.82.0 live): a NAS automount
|
||||||
|
trigger does NOT survive a guest reboot — the guest sees an empty dir; the agent has no
|
||||||
- Pre-existing (2026-07-08, unrelated): `lanresolver: cannot list provisioned guests: permission
|
network-mount reassert on guest reboot (unlike drive `ReassertGuestBinds`). Follow-up spec'd:
|
||||||
denied` — `/var/lib/felhom-agent/guests` is root-0700 under the non-root agent.
|
`felhom.eu/documentation/backlog/FOLLOWUP-nas-automount-guest-reboot-reassert.md`. NOT this
|
||||||
- `RemoveNetworkMount` leaves the empty mountpoint dir + a `not-found failed` residual unit listing
|
release — a distinct agent task.
|
||||||
(until `reset-failed`/reboot) — cosmetic, documented in the task report.
|
|
||||||
|
|||||||
@@ -103,6 +103,7 @@
|
|||||||
| Injectable seam funcs on Server | internal/localapi/server.go (`reresolveWipe`, `deviceDurableID`, `boundCheck`, net-verify: `netTrigger`/`netMounted`/`netJournal`/`netReachable`) | prod default wired in `NewServer`; tests override — no real /dev, /proc/mounts, journalctl or TCP in tests |
|
| Injectable seam funcs on Server | internal/localapi/server.go (`reresolveWipe`, `deviceDurableID`, `boundCheck`, net-verify: `netTrigger`/`netMounted`/`netJournal`/`netReachable`) | prod default wired in `NewServer`; tests override — no real /dev, /proc/mounts, journalctl or TCP in tests |
|
||||||
| Detached IN-MEMORY verify job (single slot, deliberately unpersisted) | internal/localapi/netverifyjob.go | claim slot sync (single-flight 409) → detached pipeline off baseCtx → auto-rollback on fail; restart ⇒ slot empty ⇒ the CALLER rolls back (Scenario F) — contrast formatjob (persisted+recovered) |
|
| Detached IN-MEMORY verify job (single slot, deliberately unpersisted) | internal/localapi/netverifyjob.go | claim slot sync (single-flight 409) → detached pipeline off baseCtx → auto-rollback on fail; restart ⇒ slot empty ⇒ the CALLER rolls back (Scenario F) — contrast formatjob (persisted+recovered) |
|
||||||
| Optional dependency degradation | internal/localapi/server.go (`Options`) | nil dep ⇒ endpoint answers "not configured" (503), never a crash |
|
| Optional dependency degradation | internal/localapi/server.go (`Options`) | nil dep ⇒ endpoint answers "not configured" (503), never a crash |
|
||||||
|
| Version channel (v0.82.0) | internal/localapi/server.go (`Options.AgentVersion`; `Handler()` mux wrap) | sets `X-Felhom-Agent-Version` on EVERY response (all routes/statuses, incl. auth-fail/404) — the controller's capability-comparison source; empty version ⇒ header omitted |
|
||||||
| Root-file install via random temp | internal/localapi/intermediary.go (`installSharedParentUnit`) | `stageTemp` (os.CreateTemp) → sudoers-globbed `install -m` → pinned destination |
|
| Root-file install via random temp | internal/localapi/intermediary.go (`installSharedParentUnit`) | `stageTemp` (os.CreateTemp) → sudoers-globbed `install -m` → pinned destination |
|
||||||
| Detached destructive job + restart recovery | internal/localapi/formatjob.go | persist `running` → run off baseCtx → record outcome; recovery re-resolves durable id, never a path |
|
| Detached destructive job + restart recovery | internal/localapi/formatjob.go | persist `running` → run off baseCtx → record outcome; recovery re-resolves durable id, never a path |
|
||||||
| Signed-op verify pipeline | internal/authz/verifier.go (`Verify`) | armor→namespace→key-material allowlist→crypto over RAW bytes→blob→target→window→nonce LAST |
|
| Signed-op verify pipeline | internal/authz/verifier.go (`Verify`) | armor→namespace→key-material allowlist→crypto over RAW bytes→blob→target→window→nonce LAST |
|
||||||
|
|||||||
Reference in New Issue
Block a user