Files
felhom-agent/REPORT.md
T

56 lines
4.5 KiB
Markdown

# 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`** — `runSelftestEscrowCreate` extracted into the shared `escrowCeremony()` 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 = ONE `escrow.CeremonyOutput` (v1) on stdout,
humans → stderr, `--offline`/`--paperkey` refused (exit 2).
- **The ONE fixed argv** — `internal/escrow/ceremony.go` (`CeremonyBinary` + `CeremonyArgs()`):
shared by the localapi exec, the `escrow-ceremony` capability entry (Critical, `GatedBy: pbs_dr`
explicit), and byte-identically by the new `FELHOM_ESCROW` alias in
`configs/felhom-agent.sudoers`. `TestEscrowCeremonyArgvPinned` + `TestManifestCoveredBySudoers`
lock 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 -l` grant 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:
1. one-shot wipe+latch removed → `TestEscrowCeremony_OneShotClaim` FAILED ("R holder survived the claim").
2. TTL check neutered → `TestEscrowCeremony_TTLExpiryVoidsUnclaimedR` FAILED ("claim after TTL: got 200, want 410").
3. R injected into the job struct → `TestEscrowCeremony_JobStructCannotCarryR` FAILED ("R leaked into serialized job struct").
6. argv element mutated (`--upload``--upload-mutated`) → `TestEscrowCeremonyArgvPinned` AND
`TestManifestCoveredBySudoers` FAILED (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 -l` preflight probing from the daemon works exactly as the capability prober does —
no drop-in beyond the canonical sudoers needed.