Implement governed S1 backup recovery loop
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02994-7685-7940-bf34-3555b8256018
This commit is contained in:
parent
40e295e3bd
commit
295bf43d54
16 changed files with 1623 additions and 95 deletions
95
docs/s1-backup-recovery.md
Normal file
95
docs/s1-backup-recovery.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# S1 encrypted backup and isolated recovery
|
||||
|
||||
This repository can collect only the declared S1 operating-system
|
||||
configuration and Debian package-selection evidence. It does not back up
|
||||
Kubernetes state, databases, Forgejo, application data, user homes, secrets,
|
||||
or private keys. The machine-readable boundary is
|
||||
[`spec/s1-backup.yaml`](../spec/s1-backup.yaml).
|
||||
|
||||
## Safe source gate
|
||||
|
||||
Run this without root, host access, or an age private key:
|
||||
|
||||
```bash
|
||||
make validate-s1-backup
|
||||
```
|
||||
|
||||
The declaration must agree exactly with the public age recipients in
|
||||
`.sops.yaml`. Fixture tests prove encrypted atomic publication, failure
|
||||
cleanup, receipt integrity, isolated restore, traversal rejection, freshness,
|
||||
and exact retention approval. This command never installs or starts a timer.
|
||||
|
||||
## Backup and status
|
||||
|
||||
On a host, root can create one bundle:
|
||||
|
||||
```bash
|
||||
sudo tools/cmd/railiance-backup-s1
|
||||
make s1-backup-status
|
||||
```
|
||||
|
||||
A final `s1-backup-YYYYMMDDTHHMMSSZ/` directory appears only after both
|
||||
artifacts are encrypted and the manifest and receipt are durable. The receipt
|
||||
contains hashes, sizes, host identity, source revision, declaration digest,
|
||||
and the retention decision, but no decrypted content or secret material.
|
||||
Status fails for missing, stale, over-budget, over-retained, or tampered newest
|
||||
evidence.
|
||||
|
||||
Retention is deliberately not an unattended delete. Render the current exact
|
||||
candidate set, review it, and pass back the one-use approval derived from the
|
||||
candidate receipt hashes:
|
||||
|
||||
```bash
|
||||
make s1-backup-prune-plan
|
||||
make s1-backup-prune APPROVE_S1_BACKUP_PRUNE=PRUNE-S1-BACKUPS-...
|
||||
```
|
||||
|
||||
If the set changes between those commands, pruning fails closed. Candidates
|
||||
are atomically moved beneath a quarantine directory inside the declared backup
|
||||
root before removal; paths outside that root are never accepted.
|
||||
|
||||
## Inspection and isolated restore
|
||||
|
||||
Inspection verifies receipt, manifest, declaration, encrypted hashes, sizes,
|
||||
and membership without a private key:
|
||||
|
||||
```bash
|
||||
make s1-restore-inspect BUNDLE=/opt/backup/railiance/infra/s1-backup-...
|
||||
```
|
||||
|
||||
An attended operator may then extract into an explicit absent or empty staging
|
||||
directory outside this repository:
|
||||
|
||||
```bash
|
||||
make s1-restore-isolated \
|
||||
BUNDLE=/opt/backup/railiance/infra/s1-backup-... \
|
||||
DEST=/tmp/s1-restore-drill \
|
||||
IDENTITY=/operator-controlled/path/identity.age
|
||||
```
|
||||
|
||||
The command suppresses decryption output, validates every archive member and
|
||||
mode before extraction, rejects escaping links and traversal, and writes a
|
||||
metadata-only restore receipt. It refuses `/`, `/etc`, `/home`, `/root`,
|
||||
`/var`, repository paths, and non-empty destinations. There is no live restore
|
||||
mode.
|
||||
|
||||
## Timer deployment approval interface
|
||||
|
||||
The timer runs daily at 02:15 UTC with up to 30 minutes of jitter. Its service
|
||||
can write only the declared backup root under systemd's filesystem controls.
|
||||
Deployment is a host mutation and is separate from bootstrap and verification:
|
||||
|
||||
```bash
|
||||
make s1-backup-deploy \
|
||||
HOST=Railiance01 \
|
||||
APPROVE_S1_BACKUP_DEPLOY=DEPLOY-RAIL-HO-WP-0012-S1-BACKUP-TIMER
|
||||
```
|
||||
|
||||
The Make target also refuses a dirty source tree. The playbook independently
|
||||
checks the approval and pins the deployed wrapper to the reviewed 40-character
|
||||
source revision. This approval installs and enables the timer only; it does
|
||||
not authorize pruning, off-host upload, private-key access, or a restore.
|
||||
|
||||
Off-host transfer remains `RAIL-HO-WP-0012-T05` and belongs to the governed
|
||||
`railiance-backup-offsite-lane`. An attended off-host isolated drill remains
|
||||
`RAIL-HO-WP-0012-T06`; neither action is implemented or implied here.
|
||||
Loading…
Add table
Add a link
Reference in a new issue