Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
4.0 KiB
RUNBOOK — open a retained escrow package for a household (R-304, design option C, second slice)
When: the hub mailed you
recovery_older_package— a household's recovery code opens (422), or may open (424), an OLDER sealed package that the hub keeps as retained custody (R-222), and the household asked for data from that older off-site store. The product has no path to a retained package (R-312: reopening old history is operator-only). This is that path, written down from the drill that proved it end to end (audits/DRILL-retained-key-2026-08-12.md§3.5–3.6; memory note „opening an escrow blob by hand").Promise: none new. The household's dashboard says „contact support"; this is what support does.
Rules for the whole procedure
- The household types its recovery code, on its own box. You never ask for it in a mail or chat, and it is never
on a command line or in a file outside the box's
/mnt/work folder (deleted at the end). - Nothing is printed. A recovered password is verified by its SHA-256 against the hub's record, never shown.
- Restore into a NEW folder, never over the household's live data. The household moves files afterwards.
- One package, one session; do it with the household present (phone or screen-share). Write what you did in the household's support log.
1. Find the package (DooPlex, read only)
- Snapshot the hub database with its
-waland-shm(a copy ofhub.dbalone can be hours stale). - In the copy:
SELECT id, host_id, length(identity_blob), restic_pw_sha256, superseded_at FROM host_escrow_superseded WHERE host_id='<host>' ORDER BY superseded_at DESC;— the mail names the box and the date; pick that row. - Write the blob out with
writefile(neverSELECTa BLOB to a terminal):SELECT writefile('/tmp/<host>-<id>.age', identity_blob) FROM host_escrow_superseded WHERE id=<id>; - Note the row's
restic_pw_sha256— the check value for step 3.
2. Bring it to the box
- Copy the blob into the household's guest under
/mnt/felhom-retained-<date>/(bind-mounted into the controller container at the same path —resticlives INSIDE thefelhom-controllercontainer, and a path under/rootis invisible to it). - Copy the drill's pty wrapper (a ~30-line Python
pty.fork()that runsage -d -o <out> <blob>and answers the passphrase prompt) to the same folder.age -dwill not read a passphrase from stdin; it needs the pty.
3. The household unseals it
- The household types the recovery code (the ten words) into the wrapper's prompt on its box. A wrong code fails closed at the scrypt step — no partial output.
- The output is the identity bundle. Take
restic_repo_passwordfrom it into a 0600 file in the same folder. - Check it:
sha256sumof the trimmed password must equal the row'srestic_pw_sha256(step 1.4). If not: stop — wrong package or wrong code.
4. Read the old store
- Mirror the controller's off-site arguments (
controller/internal/backup/offbox.go,offboxBaseArgs): the sftp backend with the box's ownknown_hostsandssh_key, andRESTIC_PASSWORD_FILE=/mnt/felhom-retained-<date>/pw. Run it asdocker exec felhom-controller restic …. restic snapshots— the household picks the snapshot (date, app tag).restic restore <snapshot> --target /mnt/felhom-retained-<date>/restore --include <what they need>.- Show the household the restored files. They move what they want into place through the dashboard's file tools or their share.
5. Clean up — always, also after a failure
shred -uthe password file, the bundle and the blob copy; remove/mnt/felhom-retained-<date>/once the household has its files.- Delete the hub DB copy on DooPlex.
- Note in the household's support record: the date, the package id, the snapshot, what was restored. No secret.
Negative control used in the drill: the box's CURRENT password against the old store → Fatal: wrong password or no key found. If you are unsure which store you are reading, run that first.