Files
felhom-agent/REPORT.md
T
2026-08-04 13:56:38 +02:00

3.3 KiB
Raw Blame History

REPORT — agent v0.125.0: open the sealed bundle, return one field (2026-08-04, R-199 links 78)

Released and deployed. Published sha256 f7d8339b53d92a6c45be7eaf189469a041b6b00b758a64511c0479beae7016b3, verified by independent download before install; running on demo-felhom (felhom-agent --version0.125.0, systemctl is-activeactive). NOT vouched — vouching stays the operator's act.

What shipped

  • internal/escrow/recover.goOffsiteKeyRecoverer: fetch → unseal → extract, with distinct errors for "no blob", "the bundle predates the field", and "the code did not open it".
  • internal/hub/client.goFetchIdentityEscrow (link 6's first client, ever).
  • internal/localapi/escrow_recover.go + route registration — link 7's first production caller.
  • cmd/felhom-agent/main.go — the production wiring, and the §8.6 selftest message fix.

The result

Proven live on demo-felhom, 2026-08-04: the recovered offsite repository password's sha256 equals the one on the box's disk, and equals the hash the hub had independently recorded. A deliberately wrong code five minutes earlier produced escrow: the recovery code did not unwrap the identity escrow … exit status 1 and wrote nothing — which also proves the fetch and the unseal ran independently of the success.

R persisted nowhere, searched rather than claimed: 0 agent-journal lines, 0 controller-log lines, 0 files under /tmp, /var/tmp, /var/lib/felhom-agent, /root, 0 leftover felhom-idesc-* staging dirs — with a positive control (a planted copy found, then removed and not found) so the sweep is a measurement.

Tests

go build ./... && go vet ./... && go test ./... — rc=0; agent_gates.py --fast — OK. New: internal/escrow/recover_test.go (real age crypto) and cmd/felhom-agent/escrow_recover_wiring_test.go (AST).

Red-proofs, both observed failing:

  • Returning bundle.PBSToken instead of ResticRepoPassword"the recovered key is not the sealed repository password (len 24 vs 64)". That is the mutation that mattered — every field of that bundle is a non-empty string that looks like a secret.
  • _ = escrowRecoverer with the Options.EscrowRecovery field dropped → the AST test fails with "localapi.Options … carries no EscrowRecovery field".

One red-proof did NOT work on the first attempt and is recorded, not quietly redone: the R-leak mutation wrote R into the walked directory and the test still passed, because a later failing call overwrote the leak file with a different code while the test scanned only for the first. The test was wrong. It now asserts the TMPDIR tree is EMPTY (nothing in the test creates anything there), with the content scan kept as defence in depth. Re-run: FAIL, "the unseal left 1 file(s) behind under TMPDIR". A second mutation (commenting the wiring line) failed to compile, which is not a red-proof either; the compiling version is above.

Scope

Links 68 only. Nothing installs a recovered password, reopens a repository, or restores a file — R-200's remaining half and R-201.

Observation

ageBinary is the hardcoded /usr/bin/age with no config override. Present on demo-felhom (1.2.1); a host that installs age elsewhere would fail the unseal at the last step with no configuration escape.