Master-key rotation
Deploy a dual-key ring, run the secret-rotation workflow, and retire an old key.
Tasuku rotates every sealed secret's envelope from the old active key to a new one. Rotation runs as a Cloudflare Workflow (SecretRotationWorkflow), triggered through the API: a dry run reports what would change without writing anything, and the real run processes secrets in resumable batches (100 by default, configurable 1–200 via batch_size) with compare-and-swap on each envelope's key version.
Preconditions — stop gate
Do not begin until all of these are true:
- A recent D1 backup exists (Time Travel is enough; see Backup and recovery).
- The exact current key and version are available outside the Worker's secrets.
- A new base64-encoded 32-byte key has been generated and assigned a version greater than the current version.
- You can redeploy the Worker to roll back its secrets while retaining both keys.
If any precondition fails, stop. Do not change the active version.
1. Deploy both keys
For an old version 1 and new version 2:
openssl rand -base64 32 # the new version-2 key
bunx wrangler secret put TASUKU_MASTER_KEY # paste the new version-2 key
bunx wrangler secret put TASUKU_PREVIOUS_MASTER_KEYS # {"1":"<old-version-1-base64-key>"}Then bump the version var and deploy:
// wrangler.jsonc
"vars": {
"TASUKU_MASTER_KEY_VERSION": "2",
// ...
}bun run deployVerify /readyz, GitHub App access, and one secret-backed configuration read after the deploy. Do not start rotation while any deployed version lacks the complete key ring.
2. Dry run — go/no-go gate
curl -X POST https://<your-worker>.workers.dev/api/v1/instance/secret-rotations \
-H "Authorization: Bearer $TASUKU_BOOTSTRAP_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"dry_run": true}'The response includes an id; poll it:
curl https://<your-worker>.workers.dev/api/v1/instance/secret-rotations/<id> \
-H "Authorization: Bearer $TASUKU_BOOTSTRAP_TOKEN"Proceed only when the final status is SUCCEEDED with failed_secrets: 0 and a plausible total_secrets/remaining count (a dry run never actually processes anything, so remaining equals total_secrets and processed_secrets stays 0). An unreadable envelope (failed_secrets > 0) is a hard stop — it means an older key is missing from TASUKU_PREVIOUS_MASTER_KEYS; restore it and repeat the dry run. A dry run never writes ciphertext.
3. Rotate
curl -X POST https://<your-worker>.workers.dev/api/v1/instance/secret-rotations \
-H "Authorization: Bearer $TASUKU_BOOTSTRAP_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"dry_run": false}'Poll the same GET .../:id endpoint until status is SUCCEEDED (or FAILED; the vocabulary is QUEUED/RUNNING/SUCCEEDED/FAILED, never COMPLETED). Each batch uses compare-and-swap on the envelope's key version, so the Workflow can be interrupted and resumed (or you can start a fresh rotation) without double-encrypting an already-rotated envelope.
What gets re-sealed: GitHub App credentials, user-managed secrets (${secrets.NAME} values), and every other AES-256-GCM envelope stored in D1. Sandbox leases and other short-lived encrypted state are not rotated — they expire and get resealed under the active key on their own.
4. Verify — stop gate
Repeat the dry run. The final report must show remaining: 0 and failed_secrets: 0. Then verify server readiness, GitHub App token use, a repository credential override, an MCP secret reference, and one controlled review. Keep the previous key deployed through an observation window and at least one successful backup-and-restore rehearsal.
Roll back
Never roll back by deleting the new key. Configure the old key as active and keep the new key as previous, then redeploy:
bunx wrangler secret put TASUKU_MASTER_KEY # the old version-1 key
bunx wrangler secret put TASUKU_PREVIOUS_MASTER_KEYS # {"2":"<new-version-2-base64-key>"}"vars": { "TASUKU_MASTER_KEY_VERSION": "1" }bun run deployThis reads envelopes sealed before and after the interruption. If the rollback must remain permanent, run a rotation back to version 1 before considering removal of version 2.
Retire the previous key
Previous keys are never removed automatically. Remove an old version from TASUKU_PREVIOUS_MASTER_KEYS only after a zero-pending verification, an observation window, and a restore rehearsal all pass. Redeploy, repeat application checks, and retain the escrowed old key according to your recovery policy — historical non-active secret versions are not rewritten by this workflow.