# REPORT — build-golden.sh v2.0.0: golden 0.98.3 rebuild (drill B5 + B1)

**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 | `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 (configs/build-golden.sh only)

1. **B5 — CONTROLLER_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.

## Clean-room validation (drill VM on 192.168.0.180, virgin-restored per scenario)

| 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 |

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.

## Deviations & observations

- **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.
