v0.128.0 — R-221: the escrow seed is asserted every tick, not remembered once
gates / gates (push) Failing after 28s
gates / gates (push) Failing after 28s
A rebuilt box could not run the escrow ceremony AT ALL, with no way forward from inside the product.
This was the only open item blocking a customer from something we promise them.
MECHANISM, established at file:line rather than assumed. The preflight refuses on
escrow.pbs_storage_id; the pbsdr bridge writes that key; and it wrote it from exactly one place —
finishConverged, reached only on the paths that actually converge.
The marker and the key live in different places and die at different times. The marker is host-side
(<agent-state>/pbsdr/marker.json). The key is in agent.json, which step_agent_config renders from
`base = {}` unless an explicit --preserve-from is given (felhom.eu/scripts/felhom-host-install.sh:
2396 the step, :2449 the render, :2579 the O_TRUNC write; the flag :1246, defaulting empty at :256)
— AND THE RENDER NEVER WRITES AN escrow SECTION AT ALL (grep over the whole heredoc: zero hits). So
a rebuild keeps the marker and takes the key: same descriptor, same hash, early return, and the seed
never runs again into a config that no longer has it.
A rebuild is only the case that was measured. The same hole opens for a hand-edited or restored
config, which is the honest reason this is a seam fix rather than an installer fix: the seed must be
a thing the loop ASSERTS, not a thing it did once.
Apply now re-asserts the seed BEFORE the idempotent early return. seedEscrowStorageID is unchanged
and still never clobbers a different existing value — an operator's own choice outranks the
descriptor's, with a warning naming both.
THE EARLY RETURN IS KEPT. It stops a converged box re-running Proxmox operations every 60s, and
TestSeedReasserted_OnConvergedTick_WithZeroProxmoxCalls asserts ZERO recorded runner calls on that
tick, so a "fix" that simply deleted the return would fail. Cost: one small file read plus a JSON
parse per tick, no exec, no network, early-returning once the value matches.
A seed failure can never un-converge the box: Warn plus a message on the published status, exactly
as finishConverged does it — no marker write, no state change.
Tests drive the REAL Apply with a real temp-dir agent.json and a call-recording runner; calling
seedEscrowStorageID directly cannot see the early return, which IS the defect. Production wiring
(pbsdr.NewManager(..., cfg.SourcePath, ...)) is asserted by walking main.go's AST, not by
strings.Contains, which a commented-out call also satisfies.
Red-proofs, each with the mutation asserted applied: removing the new call makes Scenario A fail on
today's tree (it did, with the intended message); removing the early return makes the
zero-Proxmox-calls assertion fail (it did).
go build / go vet / go test ./... green (29 packages), run separately from this commit.
This commit is contained in:
@@ -1,3 +1,40 @@
|
||||
## v0.128.0 — the escrow seed is asserted every tick, not remembered once (2026-08-08, R-221)
|
||||
|
||||
**A rebuilt box could not run the escrow ceremony at all, and there was no way forward from inside
|
||||
the product.** The preflight refuses on `escrow.pbs_storage_id`; the pbsdr bridge writes that key;
|
||||
and it wrote it from exactly one place — `finishConverged`, reached only on the paths that actually
|
||||
converge.
|
||||
|
||||
**The two things live in different places and die at different times.** The convergence marker is
|
||||
host-side (`<agent-state>/pbsdr/marker.json`). The key it seeds is in `agent.json`, which
|
||||
`step_agent_config` renders from `base = {}` unless an explicit `--preserve-from` is passed
|
||||
(`felhom.eu/scripts/felhom-host-install.sh:2396`, the render at `:2449`, the `O_TRUNC` write at
|
||||
`:2579`; the flag at `:1246`, defaulting empty at `:256`) — **and the render never writes an
|
||||
`escrow` section at all.** So a rebuild keeps the marker and takes the key: same descriptor, same
|
||||
hash, early return, and the seed never runs again into a config that no longer has it.
|
||||
|
||||
**Fixed by asserting rather than remembering.** `Apply` now re-asserts the seed *before* the
|
||||
idempotent early return. `seedEscrowStorageID` is unchanged and still never clobbers a different
|
||||
existing value — an operator's own choice outranks the descriptor's, with a warning naming both.
|
||||
|
||||
**The early return is KEPT.** It exists so a converged box does not re-run Proxmox operations every
|
||||
60 s, and `TestSeedReasserted_OnConvergedTick_WithZeroProxmoxCalls` asserts **zero** recorded runner
|
||||
calls on that tick — so a "fix" that simply deleted the return would fail. Cost of the re-assert: one
|
||||
small file read plus a JSON parse per tick, no exec, no network, and an early return once the value
|
||||
matches.
|
||||
|
||||
**A seed failure can never un-converge the box:** Warn plus a message on the published status,
|
||||
exactly as `finishConverged` does it — no marker write, no state change. Pinned by
|
||||
`TestSeedReassertFailure_DoesNotUnconverge`.
|
||||
|
||||
Tests drive the real `Apply` with a real temp-dir `agent.json` and a call-recording runner; calling
|
||||
`seedEscrowStorageID` directly cannot see the early return, which IS the defect. The production
|
||||
wiring (`pbsdr.NewManager(..., cfg.SourcePath, ...)`) is asserted by walking `main.go`'s **AST**, not
|
||||
by `strings.Contains`, which a commented-out call also satisfies.
|
||||
|
||||
Red-proofs: removing the new call makes Scenario A fail on today's tree; removing the early return
|
||||
makes the zero-Proxmox-calls assertion fail.
|
||||
|
||||
## (no version bump) — a comment that claimed the hub reads a field it has no field for (2026-08-08, R-260)
|
||||
|
||||
Comment-only; no behaviour, no wire change, nothing to rebuild.
|
||||
|
||||
Reference in New Issue
Block a user