make snapshot / make state-backup: the pre-flight and post-flight gates (#32) #42

Merged
pit merged 1 commit from hermes/32-make-snapshot into main 2026-10-06 16:03:55 +00:00
Owner

Summary

Two Targets package the gates the runbook carried in prose, so a service-touching
run is bracketed by commands rather than memory.

 Makefile
+snapshot       pre-flight: a Proxmox VE snapshot of the Prod Guest
+state-backup   post-flight: both Stacks' state → one encrypted tarball
 ansible/
+└── snapshot.yml   community.proxmox.proxmox_snap against vmid 141
 docs/runbooks/0001-deploy-and-rollback.md
+  §1 snapshot · §6 state-backup  (Target first, raw command beside it)
make snapshot
  source tofu/.env                 # the Proxmox creds the Guest Stack uses
  ansible-playbook -e snapshot_name=preflight-<date> snapshot.yml
    proxmox_snap(vmid=141, snapname=preflight-<date>)   # same-day re-run: no-op

make state-backup
  assert tofu/terraform.tfstate, tofu/npm/terraform.tfstate exist
  tar czf - <both> | gpg --symmetric  ──►  ~/backups/infra-forge-tfstate/tofu-<ts>.tar.enc

snapshot refuses when tofu/.env is missing; state-backup refuses when
ansible/vault-pass is missing — both before any command is composed.

Closes #32

Evidence

  • Before: step 1 was a printed reminder ("No snapshot, no run") with no
    command; step 6 said "copy an encrypted copy" with no recipe, and the existing
    artifacts had no recorded producer.

    After:

$ make snapshot
changed: [localhost]  → "Snapshot preflight-20261006 created (vmid 141, ...)"
$ make snapshot          # re-run
changed=0             → "Snapshot preflight-20261006 is already present"

$ make state-backup
wrote ~/backups/infra-forge-tfstate/tofu-20261006-175937.tar.enc
$ gpg -d … | tar xzf -
tofu/terraform.tfstate        byte-identical to live
tofu/npm/terraform.tfstate    byte-identical to live

Merge Danger

Door: two-way

The Targets add commands; they change no existing behaviour. make snapshot
creates one snapshot (and never prunes — retention: 0); make state-backup
writes a new artifact. Either is deleted by hand.

Blast Radius: low

Prod Guest estate only, and only when a human or agent runs the Target. The
restore was proven once from a fresh backup; the recipe reuses the Vault password
as the state passphrase (recorded in the runbook as the trade-off) — a leaked
Vault password now also exposes the encrypted state.

## Summary Two Targets package the gates the runbook carried in prose, so a service-touching run is bracketed by commands rather than memory. ```diff Makefile +snapshot pre-flight: a Proxmox VE snapshot of the Prod Guest +state-backup post-flight: both Stacks' state → one encrypted tarball ansible/ +└── snapshot.yml community.proxmox.proxmox_snap against vmid 141 docs/runbooks/0001-deploy-and-rollback.md + §1 snapshot · §6 state-backup (Target first, raw command beside it) ``` ```text make snapshot source tofu/.env # the Proxmox creds the Guest Stack uses ansible-playbook -e snapshot_name=preflight-<date> snapshot.yml proxmox_snap(vmid=141, snapname=preflight-<date>) # same-day re-run: no-op make state-backup assert tofu/terraform.tfstate, tofu/npm/terraform.tfstate exist tar czf - <both> | gpg --symmetric ──► ~/backups/infra-forge-tfstate/tofu-<ts>.tar.enc ``` `snapshot` refuses when `tofu/.env` is missing; `state-backup` refuses when `ansible/vault-pass` is missing — both before any command is composed. Closes #32 ## Evidence - **Before:** step 1 was a printed reminder ("No snapshot, no run") with no command; step 6 said "copy an encrypted copy" with no recipe, and the existing artifacts had no recorded producer. **After:** ```text $ make snapshot changed: [localhost] → "Snapshot preflight-20261006 created (vmid 141, ...)" $ make snapshot # re-run changed=0 → "Snapshot preflight-20261006 is already present" $ make state-backup wrote ~/backups/infra-forge-tfstate/tofu-20261006-175937.tar.enc $ gpg -d … | tar xzf - tofu/terraform.tfstate byte-identical to live tofu/npm/terraform.tfstate byte-identical to live ``` ## Merge Danger **Door:** two-way The Targets add commands; they change no existing behaviour. `make snapshot` creates one snapshot (and never prunes — `retention: 0`); `make state-backup` writes a new artifact. Either is deleted by hand. **Blast Radius:** low Prod Guest estate only, and only when a human or agent runs the Target. The restore was proven once from a fresh backup; the recipe reuses the Vault password as the state passphrase (recorded in the runbook as the trade-off) — a leaked Vault password now also exposes the encrypted state.
Two Targets package the gates the runbook carried in prose.

snapshot — a Proxmox VE disk snapshot of the Prod Guest via the installed
community.proxmox collection (ansible/snapshot.yml). It sources tofu/.env itself,
targets vmid 141 (mirroring tofu/variables.tf), and names the snapshot
preflight-<date>, so a same-day re-run finds it present and changes nothing.

state-backup — both Stacks' state tarred into one timestamped tarball under
~/backups/infra-forge-tfstate/, encrypted gpg --symmetric. The passphrase is the
Ansible layer's vault password (ansible/vault-pass), reused; the runbook records
that shared-secret trade-off. Refuses when ansible/vault-pass is absent.

The runbook documents both Targets and the recipe, and records the restore from a
fresh backup that proves it.

Verified live: `make snapshot` created a real snapshot on vmid 141 and a re-run
was a no-op; `make state-backup` wrote an encrypted tarball that restores
byte-identical to the live state files; `make -n` composes either Target and a
missing secret file stops it before any command is composed.

Refs #32
pit force-pushed hermes/32-make-snapshot from 0547fff87d to 57872f919e 2026-10-06 16:02:38 +00:00 Compare
pit merged commit 37c18356d4 into main 2026-10-06 16:03:55 +00:00
pit deleted branch hermes/32-make-snapshot 2026-10-06 16:03:55 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
olympus/infra-forge!42
No description provided.