# 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.Lookup` reload-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 401` until a manual restart). Lookup now re-reads the file once on a miss (`reloadLocked()`, factored from `load()`; idempotent under `apply`'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 one `stat`, no re-read (`loadedSize` kept accurate by in-process `Mint`). Fail-closed on an unreadable store; missing file = empty. Entirely behind the `TokenAuthority` seam — `server.go` untouched. No token/hash material logged (nothing new is logged at all). - **B2 — `guesthook.InstallSnippet` ensures the snippets dir** (`internal/guesthook/install.go`): fenced `mkdir -p /var/lib/vz/snippets` before the `install` op (fresh PVE lacks the dir; `install` without `-D` fails ENOENT — the drill's silent no-hook finding). `configs/felhom-agent.sudoers` FELHOM_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 -D` would 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 as `felhom-agent.bak-0.62.0`; **sudoers shipped WITH the binary** (CRLF-stripped, `visudo -cf` on the file AND the live /etc/sudoers context: parsed OK; the new mkdir grant present). - Service active; `--selftest` OK (incl. `pool read pool "felhom", 1 member`); daemon hub report: **agent 0.63.0, 47/47 capabilities ok, `pve:pool-read` ok** (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, sha256 `b4a89c81c33d51ea…`. **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=hub` report carries an EMPTY `capabilities` list (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. - `OpenTokenStore` calls `load()` before `MkdirAll(dir)` — harmless (read tolerates ENOENT) but the ordering reads oddly; left as-is (out of scope).