# REPORT — slice 10C: escrow consumption — productionize the spike (v0.17.0) (2026-06-10) > Overwrite-latest report. Cumulative history: [CHANGELOG.md](CHANGELOG.md). ## What was implemented The 10C spike proved the crypto + real-data restore on a key-less box (recover `K` from `(blob, R)`, restore real data, GO). 10C turns that throwaway harness into a real, tested **`Consume`** path. **Agent-only — no hub change** (10C reads its four inputs as parameters; 10D sources blob/fingerprint/ PBS-connection from the hub and prompts for R). ### `escrow.Consume(ctx, blob, R, expectedFingerprint, keyDest)` The consumption contract, baking in the spike findings: 1. **Unwrap** the blob (operating on a copy — **F-C6**: the input blob is read-only, so a failed Consume is **retryable**) with `R`; a **wrong R fails closed** at the scrypt KDF (**F-C3**) → a clear, R-free error, **nothing written**. 2. **Fingerprint gate (F-C4)** — `KeyFingerprint(recovered)` must equal the expected (the hub knows it); a mismatch **fails fast + loud, no install, no restore attempted** (the cheap correctness check *before* any multi-GB restore). 3. **Atomic install (F-C2)** at `keyDest` (`0600`, write-temp-sibling → rename); any failure leaves **no partial install**. The recovered key lives only in a `0700` tempdir that is always removed. **Secret discipline:** `R` and key bytes are never logged/persisted (only fingerprint prefixes); the input blob is read-only; `K` is never mutated. **Zero-knowledge holds**: the hub serves the blob + expected fingerprint + PBS connection; **R is by-hand from the customer, never the hub** — a hub compromise alone still cannot decrypt. ### `--selftest=escrow-consume` `-blob -fingerprint -keydest`, R via env `FELHOM_RECOVERY_CODE` (off the command line / `ps`). Invokes the **real** `Consume` live — the spike's S3 via the production path, not a harness. ## Tests (non-hollow) - valid → key installed + `KeyFingerprint(dest) == expected` + `0600` + **blob byte-unchanged**; **wrong R** → error + **no file at dest** + blob unchanged; **fingerprint mismatch** → fail fast + **no install** (the gate runs before any restore); input validation; format-tolerant fingerprint compare (no empty-fingerprint gate-bypass); atomic-install. `go test ./...` green (integration round-trip gated to a host with `proxmox-backup-client`). ## Versioning / docs - Version `0.16.0 → 0.17.0`; `CHANGELOG.md`. Doc 03 §8a (consumption implemented; zero-knowledge) + §9 (10C done) updated. No cross-repo golden (no wire change). ## Out of scope (per the task) - DR orchestration (re-enroll in restore mode, *which* guests, identity restore) → **10D**; populating/serving the restore directive from the hub → **10D**. ## Pending - **Live validation** on the demo: create a fresh escrow from the live `K` → `Consume` on a key-less scratch → restore a real encrypted backup with the consumed key; **wrong R → clean failure, nothing installed**; the live `K` stays byte-unchanged.