4.5 KiB
REPORT — v0.88.0: controller-driven escrow ceremony (agent half)
Date: 2026-07-13 · Version: felhom-agent v0.88.0 (from v0.87.0, baseline adf7882f confirmed) · Coupled: controller v0.127.0 gates its wizard on agent ≥ 0.88.0 (passive version header, header-absent = older). Commit 1c3a3ef on main. Spike basis: felhom.eu SPIKE-controller-escrow-2026-07-13 (all five mechanisms GO; the production argv adds --output=json, a sudoers-semantics-neutral delta).
What shipped
--output=json—runSelftestEscrowCreateextracted into the sharedescrowCeremony()core (cmd/felhom-agent/main.go); text mode byte-identical (banner/R/exit codes 0/1/2, incl. the upload-fail-after-R print order); json mode = ONEescrow.CeremonyOutput(v1) on stdout, humans → stderr,--offline/--paperkeyrefused (exit 2).- The ONE fixed argv —
internal/escrow/ceremony.go(CeremonyBinary+CeremonyArgs()): shared by the localapi exec, theescrow-ceremonycapability entry (Critical,GatedBy: pbs_drexplicit), and byte-identically by the newFELHOM_ESCROWalias inconfigs/felhom-agent.sudoers.TestEscrowCeremonyArgvPinned+TestManifestCoveredBySudoerslock all three. - localapi endpoints (
internal/localapi/escrow_ceremony.go):POST /escrow/ceremony(single-flight 409, detached, 60 s),GET /escrow/ceremony/status(non-secret; R structurally absent),POST /escrow/ceremony/claim(ONE-SHOT; 410 after; 10-min TTL →unclaimed_void, lazy check + AfterFunc belt),GET /escrow/preflight(6 items; staged-secret informational;sudo -n -lgrant list-probe). R in-memory only — restart loses it SAFELY (re-run supersedes).
Green gate + red-proofs (all run → fail → revert, outputs in the session record)
go build ./... && go vet ./... && go test ./... green before every commit. Test functions
668 → 678 (+9 localapi ceremony, +1 argv pin; 2 probe tests updated for the 4th gated
capability). Red-proofs:
- one-shot wipe+latch removed →
TestEscrowCeremony_OneShotClaimFAILED ("R holder survived the claim"). - TTL check neutered →
TestEscrowCeremony_TTLExpiryVoidsUnclaimedRFAILED ("claim after TTL: got 200, want 410"). - R injected into the job struct →
TestEscrowCeremony_JobStructCannotCarryRFAILED ("R leaked into serialized job struct"). - argv element mutated (
--upload→--upload-mutated) →TestEscrowCeremonyArgvPinnedANDTestManifestCoveredBySudoersFAILED (the transitive three-way lock proven). (4/5 are controller-side — see felhom-controller/REPORT.md.)
Deployed + live-validated (2026-07-13 evening)
| Target | Result |
|---|---|
| demo host 192.168.0.162 | v0.88.0 + refreshed sudoers (visudo -cf gated), restart clean, capabilities 63/63 ok, 0 degraded, 0 inactive (DR on → escrow-ceremony ok) |
| drill VM (qm 300, 192.168.0.152) | same: v0.88.0, sudoers, 63/63 ok |
| Live ceremony (drill, endpoint-exact with the REAL guest token): | stage-secret (real password, exact controller call) → preflight ALL GREEN (incl. the live sudo_grant list-probe of FELHOM_ESCROW) → POST /escrow/ceremony → done in ~4 s (2 polls @ 2 s) → status: restic_pw_sealed:true, uploaded:true, claimable, TTL 598 s, R absent from every payload → claim 1 = 200 (R: 10 words/79 chars, captured to a shell var, never printed/persisted) → claim 2 = 410 → status claimed:true, claimable:false |
| Hub-side proof | host_escrow row demo-vm-felhom-2f4b00: updated 2026-07-13 17:10:57Z, restic_pw_sha256 = 24a887bafce4… = sha256 of the guest's local repo password — the spike's hash-less blob is REPAIRED through the shipped pipeline; demo row untouched |
Method note: the wizard's agent half was validated by invoking the EXACT endpoints the controller invokes, with the real pinned-channel token (browser leg: see the controller REPORT — the drill box's dashboard password is customer-owned/Viktor's, so the typed re-auth is operator-gated).
NOT yet live-validated: behavior on a real customer box (Peti); the CLI text mode on a host (unit-locked byte-identical; not re-run live); Day-0 publish/vouch of 0.88.0 (operator follow-up — the deployed hosts got it via direct deploy).
Observations
- The v0.86.0 "3 inactive" shape on no-DR boxes becomes "4 inactive" with
escrow-ceremony— hub chip counts shift by one; no hub change needed (generic rendering). sudo -n -lpreflight probing from the daemon works exactly as the capability prober does — no drop-in beyond the canonical sudoers needed.