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

3.9 KiB

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.