slice 7 escrow: agent contract test + CHANGELOG/REPORT/CLAUDE (v0.9.0)

Agent half of slice-7 close-out finalized: cross-repo wire-contract test
(escrowUploadRequest mirrors the hub), v0.9.0 CHANGELOG entry + REPORT + CLAUDE
version line. No secrets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-10 07:45:38 +02:00
parent 47dd0bd244
commit fae11020a5
4 changed files with 104 additions and 63 deletions
+34
View File
@@ -3,6 +3,40 @@
All notable changes to **felhom-agent** are recorded here. Update on every code
change that gets pushed.
## v0.9.0 — slice 7 close-out: PBS recovery-code escrow creation (2026-06-10)
The first code that touches the PBS client encryption key `K` and introduces the customer recovery
code `R`. Default posture is **zero-knowledge**: Felhom holds an opaque `R`-wrapped blob (cannot
open it), the customer holds `R`. Grounded by `felhom.eu/documentation/tests/slice7-escrow-spike-findings.md`
(round-trip proven on a throwaway: the `R`-recovered key restores a real encrypted snapshot). Hub
opaque storage is the `felhom.eu` half (hub v0.8.0); consumption/serving is slice 10.
### Secret discipline (overriding)
`R` is `crypto/rand`, ≥128 bits, surfaced **exactly once** and **never** logged/persisted/committed;
the wrap pty's echo is discarded so `R` can't leak. `K` is read by location, **never modified** (the
live key file is byte-unchanged — Wrap operates on a copy), never logged.
### Added
- **`internal/escrow`** — `Create` generates `R` (10 EFF-wordlist words ≈ 129 bits), wraps `K` under
`R` via the **PBS-native** `proxmox-backup-client key change-passphrase --kdf scrypt`, and
**self-verifies** the blob recovers `K` (fingerprint match) before shipping. The wrap is driven
over a **stdlib pty** (`x/sys/unix`; spike F-A1 — the command is TTY-only) with **output discarded**
(F-A2 — the pty echoes the passphrase). Opt-in outputs: **(b)** `R`-wrapped offline copy (two-factor,
no extra trust) and **(a)** raw paperkey (single-factor, unrevocable — loud caveat).
- **`--selftest=escrow-create`** (`-storage`, `-paperkey`, `-offline`, `-upload`): surfaces `R` once
to stdout (never the logger), prints the opaque blob's size/fingerprint/posture, and with
`-upload` PUTs the blob to the hub (`/api/v1/hosts/{host_id}/escrow`, per-host key).
- Config: `escrow` section (`posture` default `zero_knowledge`, `pbs_storage_id`); `PBSEncKeyPath`
helper (the `<id>.enc` key K).
- Runtime dependency on the `proxmox-backup-client` CLI (the PBS key+passphrase KDF).
### Tests
- `R` entropy ≥128 / 10-word format / uniqueness; integration round-trip (wrap→unwrap fingerprint
match, **wrong-`R` fails**, **live `K` byte-unchanged**, blob ≠ plaintext key) guarded to
linux+`proxmox-backup-client`; the agent→hub wire-contract key-set (mirrors the hub's).
- **Live-validated** (demo): `escrow-create``R` (10 words) surfaced once, blob 383 B opaque,
self-verify ok, **live `K` sha256 unchanged**, exact `R` absent from stderr/journal.
## v0.8.0 — slice 7 Phase 1: unified bring-up reconcile job (provision + guest-loss DR) (2026-06-09)
The shared FRONT HALF of provision and guest-loss DR, as a journaled reconcile job mirroring the