186f3aa34e
gates / gates (push) Successful in 6m11s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
64 lines
4.0 KiB
Markdown
64 lines
4.0 KiB
Markdown
# 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)
|
||
|
||
1. Snapshot the hub database **with its `-wal` and `-shm`** (a copy of `hub.db` alone can be hours stale).
|
||
2. 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.
|
||
3. Write the blob out with `writefile` (never `SELECT` a BLOB to a terminal):
|
||
`SELECT writefile('/tmp/<host>-<id>.age', identity_blob) FROM host_escrow_superseded WHERE id=<id>;`
|
||
4. Note the row's `restic_pw_sha256` — the check value for step 3.
|
||
|
||
## 2. Bring it to the box
|
||
|
||
1. Copy the blob into the household's guest under **`/mnt/felhom-retained-<date>/`** (bind-mounted into the
|
||
controller container at the same path — `restic` lives INSIDE the `felhom-controller` container, and a path under
|
||
`/root` is invisible to it).
|
||
2. Copy the drill's pty wrapper (a ~30-line Python `pty.fork()` that runs `age -d -o <out> <blob>` and answers the
|
||
passphrase prompt) to the same folder. `age -d` will not read a passphrase from stdin; it needs the pty.
|
||
|
||
## 3. The household unseals it
|
||
|
||
1. 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.
|
||
2. The output is the identity bundle. Take `restic_repo_password` from it into a 0600 file in the same folder.
|
||
3. **Check it:** `sha256sum` of the trimmed password must equal the row's `restic_pw_sha256` (step 1.4). If not:
|
||
stop — wrong package or wrong code.
|
||
|
||
## 4. Read the old store
|
||
|
||
1. Mirror the controller's off-site arguments (`controller/internal/backup/offbox.go`, `offboxBaseArgs`): the sftp
|
||
backend with the box's own `known_hosts` and `ssh_key`, and `RESTIC_PASSWORD_FILE=/mnt/felhom-retained-<date>/pw`.
|
||
Run it as `docker exec felhom-controller restic …`.
|
||
2. `restic snapshots` — the household picks the snapshot (date, app tag).
|
||
3. `restic restore <snapshot> --target /mnt/felhom-retained-<date>/restore --include <what they need>`.
|
||
4. 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
|
||
|
||
1. `shred -u` the password file, the bundle and the blob copy; remove `/mnt/felhom-retained-<date>/` once the
|
||
household has its files.
|
||
2. Delete the hub DB copy on DooPlex.
|
||
3. 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.
|