P6 is where the CMS stops trusting that the happy path is the only path. Four slices landed; together they answer one question: if the machine dies tomorrow, what comes back?
The restore is a procedure, not a prayer
scripts/restore.sh runs six stages, each gated by a marker on disk so a half-done restore
refuses to pretend it finished:
- Prepare — decrypt the backup into scratch infrastructure
- Verify — the restored schema must match a migration set this binary knows (a backup newer than the code refuses, exit 4)
- Epoch — every environment’s epoch advances once, before anything serves. A cached generation or stale worker lease from the dead timeline can never act on the new one
- Ledger — the deletion ledger replays: everything deleted between the backup and the crash comes back as tombstones, not as ghost content
- Reconcile — the operator checks external effects (CDN purges, webhooks, site deploys) before outbox work may fire
- Serve — jobs resume, reads open — last, always last
held is the honest middle state for jobs: not queued (nobody may dispatch it), not running
(nobody holds its lease). A restore freezes every unfinished delivery there until a human says
admin restore resume.
Each dependency failure fails honestly
The load trio pauses real containers mid-run, not mocks:
- L11 — cache dies: 77,902 requests, zero failed. Reads fall back to Postgres; the rate-limiter’s Redis cell degrades rather than refusing everything
- L12 — database dies: every write fails loudly, nothing pretends to commit, jobs resume when the database returns
- L13 — purge endpoint refuses: real exponential backoff (5.2s gaps), purges stay
pending/uncertain— never relabelled done
The honest-part is worth noting: L11’s first evidence run was removed because a stale
process squatted the receiver port and the harness silently measured the wrong thing. The fix
wasn’t hiding it — load.sh now refuses to start when a squatter owns its ports, and the
truthful artifact stayed in perf/results/ as the record of the incident.
What survives the crash
The drill ran twice on the final script: 282s to capture the world, 118s to restore it, 400s
wall-clock from capture to verified serving. Epoch advanced 1→2, the post-backup deletion
replayed as a tombstone, the post-backup edit stayed absent (it was never backed up — correct),
and the in-flight job sat held until resumed. Five checks, all green, verdict
restored-and-verified, evidence stored.
The ledger exists so restore knows what was deleted
A backup taken at noon can’t know the 3pm deletion — deletion_ledger records every governed
deletion (project, collection, entry, member, role, asset) with identity-ordered high-water
marks, exports off-host, and replays idempotently. No foreign key on project_id — the row
must outlive the project it mourns. Application roles insert only through RLS; export and
replay need operator access.
Next in P6: the remaining load variants (cold cache, stampede, mixed traffic, true 10k-record data), the fault gaps (concurrent schema edits, delivery-duplicate proof), observability runbooks, then the 24-hour soak and v0.1.