docs: REPORT/CONTEXT — golden 0.98.3 rebuild validated + vouched (B5+B1 closed)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-03 18:38:20 +02:00
parent ceca355493
commit 4ba1b144d6
2 changed files with 59 additions and 68 deletions
+11
View File
@@ -5,6 +5,17 @@
## Current
- **configs: build-golden.sh v2.0.0** (2026-07-03, @ `ceca355`; no agent version change) — **drill
findings B5 + B1 FIXED** (`DRILL-golden-098-2026-07-03.md`): the controller tag is a MANDATORY
argument (the default rotted twice — a fresh install booted a pre-floor controller, forcing the
guide's manual D.1b update) and the golden now bakes a `felhom-controller-bootstrap.path` unit
(controller deploys the moment the back-half hot-plugs the bootstrap mount — no reboot; installer
v1.9.1's reboot is a redundant belt, kept). **Golden 0.98.3** baked on the drill VM, clean-room
validated (bake integrity → isolated hot-plug proof → local-golden Day-0 → published-artifact
Day-0), published (sha256 b9a02ef1…fd01) + operator-vouched — Day-0 manifest now vouches
**agent 0.63.0 + golden 0.98.3** (the v0.63.0 vouch follow-up below is DONE). Fresh installs land
current and self-manage. NEW operator follow-up (SECURITY): the customer-config `git.token` has
Gitea package-WRITE rights — scope down + rotate (evidence-doc observation O1).
- **v0.63.0** (2026-07-03, live on felhom-pve + Gitea-published sha256 b4a89c81…) — **drill findings
B3 + B2 FIXED** (`DRILL-day0-cleanroom-2026-07-03.md`): `TokenStore.Lookup` reloads the append-only
store once on a miss (cross-process coherence with the one-shot provisioner — no more fresh-install
+48 -68
View File
@@ -1,82 +1,62 @@
# REPORT — v0.63.0: B3 + B2 fresh-install fixes (token reload-on-miss + guesthook dir)
# REPORT — build-golden.sh v2.0.0: golden 0.98.3 rebuild (drill B5 + B1)
**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.
**Date:** 2026-07-03 · **Class:** implementation + clean-room drill (script-only — no Go code, no
binary, **no agent version bump**; docs-only precedent) · **Source findings:**
`felhom.eu/documentation/audits/DRILL-day0-cleanroom-2026-07-03.md` B5/B1 + the stale backlog note
`FOLLOWUP-golden-default-controller-tag.md` (now resolved). Full drill evidence:
**`felhom.eu/documentation/audits/DRILL-golden-098-2026-07-03.md`**.
## 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) |
| Base | `c9f963d` |
| Script commit | `ceca355` — configs/build-golden.sh v2.0.0 + CHANGELOG |
| Docs commit | (this one — REPORT/CONTEXT) |
| felhom.eu | guide/ledger/backlog/evidence-doc push (see that repo's REPORT) |
| Controller tag baked | **0.98.3** (reconfirmed current: CHANGELOG top + manifest HTTP 200 pre-bake) |
## What changed
## What changed (configs/build-golden.sh only)
- **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.
1. **B5CONTROLLER_IMAGE (arg 6) MANDATORY**: no default; missing → die with usage naming the
convention. Rationale in-file: the hand-bumped default rotted twice (0.43.0 → 0.85.1 → stale
again); a required arg cannot rot; auto-"latest" could bake an unvouched tag. **Red-proof:** the
no-arg invocation exits 1 with the usage BEFORE any `pct` op (run locally + in the drill VM).
2. **B1 — baked `felhom-controller-bootstrap.path`** (`PathExists=/etc/felhom-bootstrap/bootstrap.json`,
enabled, `WantedBy=multi-user.target`): starts the Condition-gated oneshot service when the
agent back-half hot-plugs the bootstrap mount into the RUNNING guest. Service unchanged
(`RemainAfterExit=yes` prevents re-trigger loops; the enabled service still covers the plain
boot case). The mp0/mp1 vzdump-inclusion guards untouched.
3. `GOLDEN_SCRIPT_VERSION=2.0.0` + `[golden]` provenance line (script version + baked tag) opening
every bake transcript. Gates: `bash -n` clean; usage renders.
## Tests & red-proofs (both run, shown failing, restored; `git diff` clean)
## Clean-room validation (drill VM on 192.168.0.180, virgin-restored per scenario)
| 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 |
| Scenario | Result |
|---|---|
| A — bake integrity | PASS: provenance line; controller 0.98.3 + infra images baked; `docker logout` + config.json removal before archive (line present, ran); vzdump **including mp0 AND mp1**; archive 583 MB, sha256 `b9a02ef1b6f02b9b58babc4c6aad9cf6c053ebdfba116c78c8e7830de757fd01`; guest 9100 destroyed |
| B — isolated hot-plug (STOP gate) | PASS: fresh restore w/o mount → service `inactive (dead)` condition-unmet, path `active (waiting)`; back-half-identical `pct set -mp9 …,mp=/etc/felhom-bootstrap,ro=1` on the RUNNING guest → service SUCCESS ~1 s later, controller `Up (healthy)` 0.98.3, `uptime -s` unchanged (**no reboot**) |
| C — local-golden Day-0 (publish gate) | PASS: served installer v1.9.1, canonical command w/o `--force-gitea-golden``[SKIP] using local golden` (resolution order); **first boot controller 0.98.3, NO D.1b**; `selfupdate/check``update_available:false` (self-manages); selftest all-OK incl. pool read; bentopdf deployed via the dashboard endpoint → 200 via traefik; hub rows: agent 0.63.0 / controller 0.98.3 |
| Publish | PUT `…/felhom-golden/0.98.3/golden.tar.zst`**201**; round-trip GET sha **matches** (publish-agent.sh pattern mirrored) |
| Vouch | operator vouched golden **0.98.3** in the Day-0 manifest (agent 0.63.0 already vouched — the v0.63.0 follow-up is DONE); confirmed via `/api/v1/artifacts` |
| D — published-artifact Day-0 | PASS: virgin box, no local golden → `fetching golden v0.98.3 from Gitea``verified sha256 b9a02ef1… matches the hub manifest` → SUCCESS; first boot 0.98.3; up-to-date. The exact fresh-customer path |
## Deploy verification (felhom-pve)
Cleanup: all drill-1 hub rows deleted (8 tables, counts 0; demo-felhom/peti-felhom intact); drill VM
reverted to `virgin` and kept; bake credential env (0600) removed. Registry read-cred handled via
env only — never logged/committed.
- 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.
## Deviations & observations
## 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).
- **Premature publish (caught + corrected):** the script's opt-in publish block triggers on the same
`REGISTRY_*` vars the pull needs — the Scenario A bake published immediately, before the Scenario C
gate. Package DELETEd (204, anon 404 verified) and re-published only after C passed. Candidate
cleanup (not done — no-"while-here" rule): a separate `GOLDEN_PUBLISH=1` opt-in.
- **SECURITY (operator follow-up):** the pull credential — the demo customer's `git.token`, i.e.
what every customer box holds — turned out to be a Gitea **admin token with package-WRITE** (the
premature publish proved it). Scope down to read-only + rotate.
- Installer v1.9.1's post-provision reboot is now a redundant belt (the path unit deploys first) —
removal recorded as a candidate installer cleanup, kept for now.
- Drill-env: `dhcpstart=10.0.2.30` + explicit `hostfwd…-10.0.2.15:22` on the QEMU command removes
the prior drill's slirp IP-collision quirk entirely.
- Guide follow-through (felhom.eu): **D.1b retired** to a one-line version check; troubleshooting
row covers pre-0.86.0 goldens; ledger B1/B5 FIXED; backlog note resolved.