Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
4.6 KiB
R-304 — the household's old recovery code and the retained packages: a one-page design (2026-10-08)
Status: design only. Nothing here is built except today's honesty fix (below). Customer data and promises are the operator's: they are the two questions at the end.
Where it stands (read in source today, not from the row)
- Retention works and the material opens the old store (drill 2026-08-12,
audits/DRILL-retained-key-2026-08-12.md). - R-311 shipped (hub v0.103.0, agent v0.129.0, controller v0.214.0): after the current package refuses a code, the
agent fetches the retained packages (
GET /hosts/<id>/escrow/retained, self-scoped, cap 16) and tries up to 6. If one opens, the screen says „the code is correct, it opens an earlier package; contact support" (HTTP 422). - R-312 is DECIDED (2026-08-13): no in-product route from the recovery screen to a set-aside store — „re-evaluate on a real customer request". So retention is an operator-only capability today, by decision.
- What was still false until today: the agent answered „the code did not open the sealed bundle" (400) also when it
had NOT tried every earlier package — the hub withheld rows (no key material, over the cap), a package was malformed,
the 6-try cap stopped the loop, or the retained list could not be read. Fixed on main today (agent 424
older_unchecked, controllerRecoveryOlderUnchecked, Hungarian + English: „we do not know whether your code is wrong … contact support"). Ships with tomorrow's releases. The fix is in the agent and the controller, not the hub: the hub never sees the code (zero-knowledge,07§2), so it cannot check a row; it already reports what it withheld (unopenable_count,truncated_count), and the agent now counts those.
„Which package?" — the question is smaller than it looked
Each escrow ceremony seals with a NEW recovery code (the household is shown it once). A code opens only the package it sealed. So when a household holds several old codes, each code selects its own package — no list, no choice screen. The agent already tries newest-superseded first. The only real limits are the two caps (16 served, 6 tried), which today's fix turns from a silent „wrong code" into an honest „not all checked".
Options for really serving the old copy
| What | Costs | Customer data / promise | |
|---|---|---|---|
| A | Keep it operator-only (R-312). The screen's „contact support" is the route. | Nothing more. The operator needs SQLite, age and a shell (the drill's §4) — slow, error-prone, undocumented as a runbook. |
No change. |
| B | In-product: when a retained package opens and carries a repository password, the screen offers a READ-ONLY browse of the old store (list + download), never a restore into place. | New surface: the old store's location (moved aside / orphaned, R-241), a second repository password in memory, a second browse path. ~2 sessions + a drill. | Changes a promise (the household can reach old history alone). Reverses R-312 — operator only. |
| C | Operator-assisted, first slice: when the agent answers 422 (opens retained) or 424 (not all checked), the controller sends ONE operator event naming the box and the package date; plus a runbook „open a retained package for a household" (the drill's §4 written down, the household types the code on its own box). | Small: one event type (operator-only), one runbook. ~½ session. | No new promise; makes the existing „contact support" true in practice. |
Pick: C now; B only on R-312's own trigger (a real customer asks). C makes the sentence the screen already says — „contact support" — something the operator can act on the same day, without reversing a decision.
First slice of C: controller: on RecoveryCodeOpensRetained / RecoveryOlderUnchecked, send recovery_retained_needed
(operator-only, warning, once per box per day) with the package date and the class — never the code. Hub: allowlist +
operatorOnlyEvents in the same commit. Docs: runbooks/RUNBOOK-open-retained-package.md from the drill's §4.
Two questions for the operator
- May the product keep promising, in the capability map and the countdown banner, that old backups „stay recoverable"? Today that is true only with your hands. If you do nothing: the promise stays worded as it is, and the honest route is „contact support" (A/C).
- Do you want C's first slice built (an operator mail when a household's code opens — or may open — an old package)? If you do nothing: nothing is built; you learn of such a household only when they write to you.