Live validation on demo-felhom caught the felhom-regen-hostkeys unit failing with 203/EXEC: ExecStart was /usr/sbin/ssh-keygen but on Debian 13 ssh-keygen is at /usr/bin/ssh-keygen. Fixed build-golden.sh, rebuilt the golden, re-validated — host keys now regenerate on first boot by the baked unit (agent issues no ssh-keygen). All three live scenarios green: provision (fresh MAC, host keys via unit, machine-id, Docker, DHCP), dr (continuity: hostname + host keys preserved), Recover (killed mid-restore -> orphan rolled back idempotently). REPORT + CHANGELOG updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.4 KiB
REPORT — Slice 7 Phase 1: unified bring-up reconcile job (v0.8.0) (2026-06-09)
Overwrite-latest report (most recent significant work only). Cumulative history lives in CHANGELOG.md. Implements
TASK — Slice 7 Phase 1: unified bring-up reconcile job. Agent-only — no hub/wire change (the new guest auto-appears in the host-report viaListLXC).
Outcome
The shared front half of provision and guest-loss DR shipped as a journaled reconcile job
(internal/reconcile/bringup.go), mirroring the slice-6 restore-test's crash-safety but KEEPING
the guest on success and applying a scenario-specific identity policy. Built from the slice-7
spike findings (commit 3342993): F1 (MAC reset unconditional on provision), F3 (host keys via a
baked golden first-boot unit, not an agent guest-internal op), F4 (transient config-lock retry).
What landed
RunBringUp(BringUpSpec) BringUpResult— restore → identity reset → size → attach mounts → start LINK-UP, each mutation preceded by journaling the owning entry. Verdict is liveness (waitRunning), never the start exitstatus (reuses the v0.7.0 WARNINGS surface). Success keeps the guest (the key difference from the restore-test).- Identity policy (doc 03 §9): provision resets MAC unconditionally (
PUT net0, hwaddr omitted → PVE regenerates; F1) + hostname, host-side via the token; machine-id + SSH host keys regenerate guest-side on first boot (systemd + the baked unit) — the agent never touches guest internals. dr_guest_loss preserves continuity (keep hostname; keep MAC unlessKeepMAC=false); never resets restic/tunnel/hub identity. - Compensating rollback: any mid-flight failure destroys the just-created guest
(
ClassGuestDestroybenign viaProvenance{SameTxnCreated:true}, gated). New journal flagRollback+Recover.recoverBringUpreap a half-built guest from a mid-job crash (idempotent, viaListLXC) — distinct from the scratch path's audit label, same destroy machinery. - F4: identity+sizing+mounts coalesced into ONE
PUT config; rootfs grow kept separate;setConfigWithLockRetryretries ONLY the transient PVE config-lock 500 (pveConfigLock), never a real error. --selftest=bring-up(-mode provision|dr -archive -vmid -hostname [-keep]) — runs the real job after aRecover, then tears the guest down unless-keep.configs/build-golden.sh— the validated golden recipe incl. the F3 first-bootfelhom-regen-hostkeys.service(Condition-gated: fires on provision, no-ops on DR). The spike's golden archive (no unit) is superseded.
Tests (assert the effect)
go test ./... green; -race green on the build server. Provision happy path (fresh MAC = net0
without hwaddr, hostname, coalesced sizing+mount, rootfs-grow separate, started, guest NOT
destroyed); compensating rollback injected at each step (restore / config / start-task /
waitRunning → asserts the guest was destroyed); DR continuity (MAC kept, hostname not reset) +
DR KeepMAC=false resets MAC; liveness verdict (warnings+running pass / not-running fail); F4
(lock-500 → retry → proceed; non-lock 500 → fail without retry); owning entry journaled before
restore; reserved/existing VMID refused; Recover rolls back / clean.
Live validation (demo-felhom) — all green; caught + fixed one real bug
Golden built via configs/build-golden.sh → local:backup/vzdump-lxc-9100-2026_06_09-21_32_58.tar.zst
(Docker overlayfs, host-key unit baked + enabled). All runs via --selftest=bring-up.
- Bug caught live (then fixed + re-validated): the first golden's
felhom-regen-hostkeysunit usedExecStart=/usr/sbin/ssh-keygen(as drafted in the task) →status=203/EXEC("No such file"); on Debian 13ssh-keygenis at/usr/bin/ssh-keygen. Fixed inbuild-golden.sh, rebuilt the golden, re-validated. (The unit tests mock the guest, so only the live run could surface this — exactly why the slice runs live.) - provision (vmid 8100): restore → fresh MAC
BC:24:11:4A:CF:3C(≠ golden's) → hostnamefelhom-prov-8100→ link-up, 8s. Verified:felhom-regen-hostkeysactive and SSH host keys present (root@felhom-prov-8100) — regenerated by the baked unit, the agent issued no ssh-keygen;ssh.serviceactive; machine-id unique (103a0325…); hostname propagated; clean DHCP lease192.168.0.114;docker run hello-worldOK → torn down. - dr (vmid 8101, from a 9999 customer backup): continuity preserved — hostname stayed
felhom-selftest-scratch(NOT reset) and SSH host keys preserved (not regenerated). The source had no net0, so MAC-keep was a no-op live (covered by the unit test instead). - Recover (vmid 8102): killed a provision mid-restore (
timeout -s KILL); the server-side restore left an orphan guest; the re-run'sRecoverloggedrolled back half-built bring-up guest vmid=8102(BringUpRolledBack:1), then re-provisioned cleanly → torn down. Idempotent.
Teardown: no spike guests linger; the validated golden is retained as the artifact; the service runs v0.8.0.
Deferred (stated, not built)
Provisioning BACK HALF (controller deploy, bootstrap, per-guest token mint) → slice 8; host-loss
DR + escrow consumption → slice 10; the SOURCE of a BringUpSpec (hub desired-state) → slice 10
(GuestMount defined minimally, no hub coupling). No secrets committed.