58 lines
3.3 KiB
Markdown
58 lines
3.3 KiB
Markdown
# REPORT — agent v0.125.0: open the sealed bundle, return one field (2026-08-04, R-199 links 7–8)
|
||
|
||
**Released and deployed.** Published sha256 `f7d8339b53d92a6c45be7eaf189469a041b6b00b758a64511c0479beae7016b3`,
|
||
**verified by independent download** before install; running on demo-felhom (`felhom-agent --version`
|
||
→ `0.125.0`, `systemctl is-active` → `active`). **NOT vouched** — vouching stays the operator's act.
|
||
|
||
## What shipped
|
||
|
||
- `internal/escrow/recover.go` — `OffsiteKeyRecoverer`: fetch → unseal → extract, with **distinct**
|
||
errors for "no blob", "the bundle predates the field", and "the code did not open it".
|
||
- `internal/hub/client.go` — `FetchIdentityEscrow` (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 6–8 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.
|