RECON: trace the offsite DR chain link by link — it does not join up (R-198..R-201)
gates / gates (push) Successful in 7s

Read-only recon of the escrow -> recovery chain, from a dead node to an open
repository. No production code, no build, no version bump.

Headline: the hub's superseded-escrow retention does NOT retain the offsite
repository password. host_escrow_superseded has no identity_blob column and
demoteCurrentEscrowTx copies only the K-escrow blob, so what survives a
supersession is the PBS datastore key, not the restic repo password. The next
escrow ceremony -- which the system tells a rebuilt box's customer to run --
destroys the last copy. Both demo boxes crossed that line on 2026-08-04.

Also established:
- the hub's blob-serving endpoints (re-enroll / restore-directive) have zero
  callers anywhere: agent, hub UI, scripts, runbooks (R-199)
- POST /backup/offbox/inject-password is routed and handled but no template
  contains the form (R-200)
- nothing in the recovery path has ever been exercised; the one live
  round-trip proof (2026-06-10) predates the ResticRepoPassword field (R-201)
- a fail-closed mint refusal IS implementable: the report ACK already carries
  escrow{identity_blob_present, restic_pw_sha256} and the controller discards
  it whenever no offbox target exists

Corrections: yesterday's spike annotated (candidate (b) overturned in part --
unattended recovery is impossible, customer-present is not); capability-map
retention claim struck through and replaced with what the code does.

Deliverable: documentation/audits/RECON-offsite-dr-chain-2026-08-04.md
Register: new R-198..R-201; R-193 and R-192 updated; STATUS.md refreshed.
This commit is contained in:
2026-08-04 12:16:04 +02:00
parent d26f49ad68
commit 3f2b7bc023
6 changed files with 726 additions and 310 deletions
@@ -10,6 +10,39 @@ what it measured) · **Class:** spike — **no production code shipped for R-193
---
> ### ANNOTATION 2026-08-04 (later the same day) — read this before acting on §Q8
>
> A follow-up recon traced the escrow→recovery chain link by link
> (`audits/RECON-offsite-dr-chain-2026-08-04.md`). **Two corrections to this document, and they pull
> in opposite directions.**
>
> **(1) Q8 candidate (b) — "not implementable as specified" — is TOO STRONG and is overturned in
> part.** Zero-knowledge means the *hub* cannot open the blob; the *customer* can, with R, which is
> what the ceremony exists for. The mechanism to hand a rebuilt box its old repo password back —
> customer-present, not unattended — is a real design, and the operator has since ruled on its shape.
> What (b) got right is narrower than what it said: **an UNATTENDED rebuild cannot recover from a
> zero-knowledge escrow.** A customer-present one can. The recon prices that shape row by row.
>
> **(2) A LOAD-BEARING FACT THIS SPIKE DID NOT CHECK, and it makes (d)-alone weaker than ranked
> here.** `host_escrow_superseded` **has no `identity_blob` column**, and `demoteCurrentEscrowTx`
> (`hub/internal/store/store.go:2547-2556`) copies only the K-escrow `blob`. So the retained
> "superseded" blob holds the **PBS datastore key**, not the **restic repository password**. The
> retention feature does not retain the secret it was built to retain, and the **next escrow ceremony
> — which the system tells the customer to run whenever a rebuild leaves the escrow `pending` — is
> what destroys the last copy.** Both demo boxes crossed that line on 2026-08-04 at 07:15:36 and
> 07:20:08. → **R-198**.
>
> **Consequence for this document's recommendation.** "Ship (d), then decide (c)" still holds, but
> (d)'s implicit promise — *the old history stays recoverable with the recovery code* — is **false as
> the code stands**, and is repeated verbatim to the customer in Hungarian on the orphan card. (d) is
> not complete without R-198.
>
> **Unchanged and still correct:** Q1, Q2, Q4 (R-196), Q5, Q6, Q7 (R-192) and the Q8 (a)/(c)/(d)
> analyses. **Q3 remains UNMEASURED** — neither box has run since; the 2026-08-05 ~02:15 UTC run is
> still the decisive one.
---
## 0. Why this exists
Two candidate fixes were named when R-193 was filed, and neither could be weighed, because two prior
@@ -412,6 +445,12 @@ re-keyed, `host_leaf_changed` 2026-08-03 06:09:40), so re-enroll returns the exi
### (b) The credential becomes recoverable from escrow at re-bootstrap
> **ANNOTATED 2026-08-04 (recon) — this ruling is overturned in part. See the annotation at the top.**
> The constraint is real but narrower than written: an **unattended** rebuild cannot recover from a
> zero-knowledge escrow; a **customer-present** one can, and the operator has ruled on that shape.
> The recon also found that the chain this option would use is clientless at three links and lossy at
> a fourth (`RECON-offsite-dr-chain-2026-08-04.md` §3–§4).
- **BLOCKED AS STATED, and this is a hard constraint, not an estimate.** The escrow blob is
**R-wrapped and zero-knowledge** — the hub holds opaque bytes and has no recovery code
(`api/handler.go`, and D6 in `CONTEXT.md` S-3). **A rebuilt box cannot unseal it without the