Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
6.0 KiB
REPORT — v0.63.0: B3 + B2 fresh-install fixes (token reload-on-miss + guesthook dir)
Date: 2026-07-03 · Class: implementation, correctness+security discipline (agent v0.62.0 →
v0.63.0, live on felhom-pve) · Source findings:
felhom.eu/documentation/audits/DRILL-day0-cleanroom-2026-07-03.md B3/B2.
Baselines & commits
| What | Value |
|---|---|
| Base | 84f3f7d (v0.62.0, as spec'd) |
| Fix commit | f31a76f — v0.63.0: B3+B2 (tokenstore.go, guesthook/install.go, sudoers, tests, CHANGELOG, REUSE) |
| Docs commit | (this one — REPORT/CONTEXT) |
What changed
- B3 —
TokenStore.Lookupreload-on-miss (internal/localapi/tokenstore.go): the one-shot provisioner Mints into the shared append-only JSONL while the daemon serves Lookup from an index built once at open — the daemon 401'd any token minted after it started (the drill's/controller/swap: HTTP 401until a manual restart). Lookup now re-reads the file once on a miss (reloadLocked(), factored fromload(); idempotent underapply's last-write-wins) and re-checks with the same constant-time compare, all under the existing single mutex. Append-only size short-circuit: an unknown token against an unchanged file costs onestat, no re-read (loadedSizekept accurate by in-processMint). Fail-closed on an unreadable store; missing file = empty. Entirely behind theTokenAuthorityseam —server.gountouched. No token/hash material logged (nothing new is logged at all). - B2 —
guesthook.InstallSnippetensures the snippets dir (internal/guesthook/install.go): fencedmkdir -p /var/lib/vz/snippetsbefore theinstallop (fresh PVE lacks the dir;installwithout-Dfails ENOENT — the drill's silent no-hook finding).configs/felhom-agent.sudoersFELHOM_GUESTHOOK gains exactly the one grant/usr/bin/mkdir -p /var/lib/vz/snippets(comment ties it to B2). Kept the separate-mkdir shape —install -Dwould have changed the allowlisted install argv.
Tests & red-proofs (both run, shown failing, restored; git diff clean)
| Test | Asserts | Result |
|---|---|---|
TestTokenStore_ReloadOnMiss_CrossProcessMint (Scenario A) |
token minted by a SECOND store handle after the daemon handle opened resolves to (120,true) | PASS. Red-proof: pre-fix Lookup shape (plain miss, no reload) → FAIL with got (0,false), want (120,true) — the exact drill 401 |
TestTokenStore_ReloadOnMiss_BoundedReloads (Scenario B) |
unknown token → (0,false); exactly 1 reload after an external append; 0 further reloads on an unchanged file (size short-circuit) — no loop, no per-candidate re-read | PASS |
TestTokenStore_ReloadOnMiss_RemintCoherence (Scenario C) |
cross-process rotation: new hash → (120,true), rotated-out hash 401s, byHash/byVMID coherent after full reload |
PASS |
TestTokenStore_ReloadOnMiss_MissingFile (§8 edge) |
store deleted → fail closed, no crash | PASS on linux (felhom-pve run); skipped on Windows (can't unlink an open handle) |
TestInstallSnippet_EnsuresSnippetsDirFirst (Scenario D) |
a mkdir -p /var/lib/vz/snippets op with exactly the sudoers-fenced argv PRECEDES the install op |
PASS. Red-proof: mkdir call removed → FAIL with no mkdir -p /var/lib/vz/snippets op issued … calls: [[install …]] |
| Pre-existing suites | tokenstore_test.go originals, TestInstallSnippet_RandomTempName (only adapted to filter install calls from the recorded vector — assertions unchanged), full repo |
all PASS; green gate go build && go vet && go test ./... exit 0 |
Deploy verification (felhom-pve)
- Built on 180 from
f31a76f; binary backed up asfelhom-agent.bak-0.62.0; sudoers shipped WITH the binary (CRLF-stripped,visudo -cfon the file AND the live /etc/sudoers context: parsed OK; the new mkdir grant present). - Service active;
--selftestOK (incl.pool read pool "felhom", 1 member); daemon hub report: agent 0.63.0, 47/47 capabilities ok,pve:pool-readok (host_reports id 2412). No reassert/rebind or error lines in the journal after restart. - Published to Gitea:
felhom-agent/0.63.0, HTTP 201, sha256b4a89c81c33d51ea…. OPERATOR FOLLOW-UP: bump the hub Day-0 artifact manifest to agent 0.63.0 (password-gated UI — CC cannot). Until then, fresh installs fetch 0.62.0 and still need the D.1b restart.
Scenario E method + result (stated per §7-E; live store NOT polluted)
felhom-pve's guest 9201 already has its token in the daemon index, so a live swap cannot reproduce
the fresh-install miss, and minting into the production store is forbidden. Method used (the spec's
sanctioned alternative): (1) compiled the localapi test binary on 180 and ran the full TokenStore
suite ON felhom-pve — all 4 reload-on-miss tests PASS on the real platform, including the
linux-only deleted-file case; (2) live hit-path/no-regression: the in-guest controller's
agent-channel-health scheduler job completed cleanly every minute after the restart (no 401, no
channel event) — the deployed daemon authorizes the existing token exactly as before.
Guide follow-through (felhom.eu)
D.1b's unconditional "restart the agent first" step was narrowed, not dropped: the fix is deployed and platform-proven, but the Day-0 manifest still vouches agent 0.62.0, so a box installed today still gets a pre-fix agent. The guide now says: restart first only when the installed agent is < v0.63.0; the troubleshooting row records the fix version. Drill ledger B2/B3 marked FIXED v0.63.0. (The step drops entirely once the manifest vouches ≥ 0.63.0.)
Observations (not acted on)
- The one-shot
--selftest=hubreport carries an EMPTYcapabilitieslist (the probe is composed only into the daemon loop) — a hub-side capability checker reading the newest report could transiently see 0 capabilities after an operator runs the one-shot. Cosmetic today; worth a look when touching the capability checker. OpenTokenStorecallsload()beforeMkdirAll(dir)— harmless (read tolerates ENOENT) but the ordering reads oddly; left as-is (out of scope).