Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ the clean and disposable failure fixtures locally.
plans. The maintainer-approved exception is authenticated ciphertext for
essential bootstrap recovery in `recovery/`, bound by its registered package
validator and an independently recoverable key. Ordinary service credentials
stay in OpenBao. The current package covers a historical DNS snapshot only;
adding state recovery requires its own reviewed format and recovery proof.
stay in OpenBao. Paired state packages bind encrypted state and desired
configuration to their reviewed source and run; authenticate recovery with
the independently held identity before using either file.
- Import every existing resource before any apply. During migration, convert
the plan to JSON and run `scripts/assert-no-destructive-plan.sh`; any delete
or replacement is a hard stop.
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ The retained pre-migration DNS snapshot lives as authenticated ciphertext in
Keychain. This historical snapshot is neither current desired configuration nor
OpenTofu state; the runbook describes its limits and recovery checks.

The paired package in [`recovery/state/`](recovery/state/) retains captured
OpenTofu state and its complete desired configuration. Its manifest binds both
encrypted files to their source and run. Verify authenticated recovery before
using a generation, and refresh it after each reviewed apply. Provider convergence
and remote backend locking have separate acceptance checks in the runbook.

In those procedures the provider reads `CLOUDFLARE_API_TOKEN`, and the state
backend reads the standard `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`
variables. Keep all three out of shell history and plaintext repository files.
Expand Down
9 changes: 6 additions & 3 deletions docs/runbooks/bootstrap-and-recovery.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

This procedure keeps Cloudflare recovery independent of the Kubernetes
platform. It never reads credentials from a cluster-hosted secret and never
writes credentials or state into this repository.
writes plaintext credentials or state into this repository. Essential encrypted
state and configuration recovery packages use the custody procedure below.

## Prerequisites

Expand Down Expand Up @@ -311,8 +312,10 @@ Active state still belongs in the independently accessible locked R2 backend.
Its minimum authenticated encrypted recovery copy belongs with this owner,
including source revision, lineage and serial bindings. That state format,
publication path, actual lock-contention test and clean-environment recovery
remain part of the first migration. No real state recovery package is currently
retained, and no apply automation is enabled by the packaging helpers.
remain part of the first migration. The retained initial generation contains the
runner-local zone import and matching desired configuration from the confirmed
bootstrap plan; it does not establish provider convergence or remote locking.
No apply automation is enabled by the packaging helpers.

### State recovery format

Expand Down
Binary file added recovery/state/configuration.json.age
Binary file not shown.
16 changes: 16 additions & 0 deletions recovery/state/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"kind": "cloudflare-state-recovery",
"capturedAt": "2026-11-02T00:46:14Z",
"sourceCommit": "0925d157610d9e281e07557e689a321dadc791e9",
"sourceRun": 41368905763,
"recipient": "age1rk6fs67kly3h4zux5za429z3grjtvs8vcunav4sa28pxk738najsk8wgs6",
"lineage": "d981e029-9986-63a7-2f54-379ce10ee6a4",
"serial": 1,
"accountIdSha256": "3080d90d2ff8a5d38ec8c5dae7b9207b928b353c68f4dd81e0a378cf932a5f95",
"zoneNameSha256": "1fc6c364e0d592421f88ba2d2cf6cf15691bbc711113d7907c159a638f35aeb0",
"stateCiphertextSha256": "4dd0e162814c478c0d675e034eec887de40a382a96f9bb251afb841fabe2709f",
"configurationCiphertextSha256": "5c9527c73d0bfacf126939b555a609c6895ca98cbeb044d7793a33ee65460030",
"statePlaintextSha256": "bf7b431748872f53861aa655cb40c0f2734410f03514e97de5f30ea0aaf69442",
"configurationPlaintextSha256": "84c9b6ea504f82a9fd694e701287e30d54fe9b42ecf81dfccfbf2cba9873239a"
}
Binary file added recovery/state/state.json.age
Binary file not shown.
Loading