railiance-platform/docs/backup-credential-recovery.md
codex 5ef016be01
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
Contain backup upload credentials and prepare provider recovery gates
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
2026-09-05 19:21:06 +02:00

67 lines
3.9 KiB
Markdown

# WP-0029 provider recovery procedure
Scope: invalidate the exposed Nextcloud upload predecessor and prove replacement
encrypted upload and offsite recovery under CCR-2026-0004. The route is a
Nextcloud file-drop share, not a platform-admin OpenBao credential. OpenBao
administration does not confer Nextcloud share-owner authority.
## Required owner inputs
- Nextcloud share owner/account and the reviewed share-management session.
- A replacement file-drop share/token delivered through protected custody;
never chat, argv, Git or State Hub. Record only custody coordinates.
- Owner-authorized retrieval access for the encrypted offsite artifact. Do not
assume an upload-only file-drop token can list or download backups.
- A protected reference to the predecessor for a bounded rejection probe, or
accepted provider-side revocation evidence. Do not reconstruct it into logs.
## Execution order
1. Confirm current OpenBao KV version and preserve every existing field. The
lane contains `AGE_PRIVATE_KEY`; replacing only upload fields must use CAS
and preserve recovery escrow. Never use a partial `kv put` that drops it.
2. Confirm the exact Nextcloud share and all backup consumers. Besides the
workstation helpers, activity-core consumes `NC_WEBDAV_TOKEN` and
`NC_WEBDAV_URL` through ExternalSecret `actcore-backup-offsite`. Coordinate
its next invocation so process-cached credentials cannot produce a false
replacement-success claim.
3. Create the replacement under provider authority. Verify its permissions
match the file-drop lane; no production delete/admin authority is needed.
Seed both upload fields through contained OpenBao CAS and verify delivery
with boolean comparisons. Keep provider secrets out of evidence.
4. Upload a uniquely named age-encrypted artifact with the replacement. Require
HTTP 200/201/204; redirects and network errors are not success. Record only
artifact identity and safe status. A synthetic fixture can prove transport,
but it does not establish Forgejo application recovery.
5. Invalidate the predecessor at the provider. Verify rejection with the old
credential in protected memory against the same bounded operation. A 401/403
or accepted provider invalidation receipt is evidence; a timeout is not.
Preserve existing retained backup objects and access to their encryption key.
6. Retrieve the newly uploaded encrypted artifact through the separate owner
recovery path, decrypt in a protected temporary directory, and verify its
expected content. For Forgejo restore acceptance, use a fetched real backup
and the owning isolated restore procedure with repository/blob/database
checks. Require the local fetched artifact to exist before invoking the
historical infra drill: that script otherwise creates a fresh production
dump, which cannot prove offsite recovery.
7. Record provider invalidation, replacement KV version, upload/download,
decryption, isolated restore and cleanup receipts. Mark T02 done only after
the agreed recovery checks pass. No receipt may contain the predecessor's
value, fingerprint, length or shape.
## Recovery-key exposure remains separate
CCR-2026-0004 records historical exposure of the age recovery key as well as
upload credentials. Upload-share rotation alone must not clear that taint.
Retain old age recovery material until all retained artifacts are accounted for
and any owner-approved re-encryption is complete. Do not silently rotate or
discard the age key as part of this upload-token repair.
## Prepared transport
`lib/railiance-backup-common.sh` passes curl credentials and credential-bearing
URLs via stdin configuration, disables curlrc, suppresses backend diagnostics,
requires HTTPS and rejects non-success responses. The Python backend streams
the artifact, refuses redirects and suppresses credential-bearing exception
text. These source changes are preparation; no provider rotation, upload or
restore is claimed by their tests.