Every approved change was executed and undone on a shadow copy first, with the checksums attached.
We prove migrations, erasures, refunds and access grants against a shadow copy, then hand you the checksums and let you decide.
From the first line of SQL to a sealed, tamper-evident receipt — nothing reaches production unproven.
// packages/contract/src/gate.ts
const GATE_WITNESS: unique symbol = Symbol('airlock.gate.witness');
export interface ApprovalGrant {
readonly [GATE_WITNESS]: true; // unforgeable outside this module
}Not greyed out, not hidden behind a warning. A disabled button is a decision you can argue with; an unrepresentable state is one you cannot.
Attempts to fake a grant are asserted as type errors. Weaken the type and tsc reports an unused @ts-expect-error — the build fails.
The gate re-runs server-side against the stored dossier. Approving through the HTTP API with no browser involved is refused identically.
PROVEN — ROLLBACK VERIFIED
Applied and rolled back on a shadow branch. The data returned byte-identical.
Add a tier column to users, backfill it, then drop the deprecated plan_name column.
openGate() returned a grant, so the control above can exist. It is the only way one comes into being.
openGate(dossier, { role: 'approver' })
-> { state: 'OPEN', grant: { final: true, seals: 0/1 } }Apply the change to a shadow branch. Apply the rollback. Checksum the affected tables a third time and prove the data came back byte-identical.
AIRLOCK never trusts the verifier’s own match flag. It recomputes pre === post_rollback itself, so an engine bug cannot open the door.
Every record that will be destroyed, across every system — plus an explicit list of what is being deliberately kept and the obligation justifying each exclusion.
An exclusion with no stated reason is rejected by the contract. “We kept some things” is not a scope.
| Class | Certificate | Approvers | Proof valid | Undo window | Break-glass |
|---|---|---|---|---|---|
| Schema migration | UNDO | 1 | 30 min | 30 min | permitted |
| Data operation | UNDO | 1 | 30 min | 15 min | permitted |
| Erasure | SCOPE | 2 | 15 min | none | no |
| Access grant | SCOPE | 2 | 10 min | none | permitted |
| Money movement | SCOPE | 2 | 10 min | none | no |
| Comms blast | SCOPE | 2 | 15 min | none | no |
| Infrastructure mutation | ANY | 2 | 15 min | 10 min | permitted |
Note what is deliberately absent: there is no change freeze on erasure, money movement or access grants. A freeze that blocks a right-to-erasure request trades a legal problem for an operational one. That absence is asserted in the test suite, so it cannot be quietly reversed.
Production is re-checksummed the moment the change lands and compared against what the certificate predicted it would become.
A mismatch executes the inverse that was already demonstrated. Where it was never proven, AIRLOCK refuses and raises an alarm instead.
Most bad changes are perfectly healthy by every checksum and simply turn out to be wrong. Only a person ever catches that.
The refusal is the feature. AIRLOCK will only auto-revert a rollback it has proof of. Running an untested inverse against a database already in an unexpected state is how a bad afternoon becomes a bad quarter — so that case stops and gets a human.
COMPUTING…
Hashing three records into a chain with the Web Crypto API.
sha256:0000000000000000000000000000000000000000000000000000000000000000
Keep this hash somewhere we cannot reach — a wiki, an email to yourself, an auditor’s file. Any future edit to any record above changes it, and you will be able to tell without trusting us about anything.
This does not make the ledger unforgeable — anyone who can rewrite the file can recompute the whole chain. What it makes is tampering visible to anyone holding an older copy of a single hash.
That is the property that actually matters, because the person auditing you is not the person who edited it.
AIRLOCK is the literal answer — an agent that behaves as though it is not trusted with root, and proves it every time before it asks.