Change control for agents with production access

“Approve?”is the wrongquestion.

An agent asks to drop a column on a hundred thousand rows. Nobody can answer that honestly — not without knowing whether the rollback actually works.

So AIRLOCK does not ask it. It runs the change against a copy of your database, undoes it, and checksums all three states. The gate opens only if the data came back.

npm run demo— three real changes, ~90s

Every other approval flow

The agent wants to:

alter table users
drop column plan_name

100,000 rows · 1 dependent index

certificate
— not attached
rollback proof
— not attached
blast radius
— not attached
ApproveReject

You are being asked to trust a plan. Nothing on this screen tells you whether the rollback works.

AIRLOCK

gate sealed

Ran it against a copy of the real rows. Then ran the rollback.

pre
sha256:d2f21cbcb608ed22…350d21
post
sha256:62dd725050452f1b…e4cc83
post-rollback— not line 1
sha256:ad8449f8165a8679…2ecfc4

no approval control exists

Line 3 is not line 1. The column came back; the hundred thousand values in it did not.

Not greyed out — never rendered. The value that would represent permission cannot be constructed, so the agent has nothing to offer a human and nobody is interrupted about a change that cannot be undone.

  1. 01The agent opens

    Writes the change and its inverse. No certificate yet, so the gate is shut by default.

  2. 02AIRLOCK proves

    Runs it on a copy of the real rows, runs the rollback, checksums all three states.

  3. 03A human decides

    Only reachable from a passing proof. The agent has no tool that applies anything.

0
tools that write to production
1,000,000
rows the demo proves against
347
tests
23
harness capabilities
Runs onTrueForgeAI GatewayNomaQodoGitHub MCPDaytonaSupabaseExaBright DataTogether AIFireworksOpenUI
02 / 08The rule

The gate is not a conditional. It is a type.

Every other approval flow is “the agent says it is going to do X — click yes.” That asks a human to trust a plan. AIRLOCK’s gate cannot be offered until the agent holds a certificate: the result of having actually done the thing, and undone it, somewhere safe.
// 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
}
i

Never rendered

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.

ii

Six forgeries, six compile errors

Attempts to fake a grant are asserted as type errors. Weaken the type and tsc reports an unused @ts-expect-error — the build fails.

iii

curl gets the same answer

The gate re-runs server-side against the stored dossier. Approving through the HTTP API with no browser involved is refused identically.

03 / 08Try the gate

Try to open it.

The controls below build a real Change Dossier and pass it to the real openGate() — the same function the console calls and the server re-runs before it writes anything. Every combination is a live evaluation. See if you can find one that opens a door it should not.
Try to open it1 tried · 1 opened
Change classeach class has its own policy
Certificate
The checksum tripledid the data come back?
Proof age
Production
You are

PROVEN — ROLLBACK VERIFIED

Applied and rolled back on a shadow branch. The data returned byte-identical.

What the console rendersSCHEMA_MIGRATION

Add a tier column to users, backfill it, then drop the deprecated plan_name column.

certificate
UNDO · PROVEN
proof age
2 min
production
unchanged
magnitude
1,200,000 rows
signatures
0 of 1
policy
satisfied

openGate() returned a grant, so the control above can exist. It is the only way one comes into being.

the call that produced the above
openGate(dossier, { role: 'approver' })
-> { state: 'OPEN', grant: { final: true, seals: 0/1 } }
04 / 08Two kinds of proof

You cannot prove a deletion reversible.

So for the changes that genuinely cannot be undone, the agent proves the opposite thing instead — not that you can take it back, but that it knows exactly what “it” is.
UNDOfor reversible work

Three checksums

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.

presha256:0234ab62abae86f9…
postsha256:9f11c7de40b1c882…
post-rollbacksha256:0234ab62abae86f9…match

AIRLOCK never trusts the verifier’s own match flag. It recomputes pre === post_rollback itself, so an engine bug cannot open the door.

SCOPEfor the irreversible

Exactly what dies

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.

DESTROYpostgres — 4 subjects across 12 tables168
DESTROYstripe — customer + payment methods4
KEEPinvoices — seven-year statutory retention47

An exclusion with no stated reason is rejected by the contract. “We kept some things” is not a scope.

05 / 08Policy

Allowed, by whom, and right now?

A certificate answers “is this change what it claims to be”. It cannot answer whether your organisation permits it — that is not a property of the change. Seven classes, each with its own rules, in a YAML file a team can argue with.
ClassCertificateApproversProof validUndo windowBreak-glass
Schema migrationUNDO130 min30 minpermitted
Data operationUNDO130 min15 minpermitted
ErasureSCOPE215 minnoneno
Access grantSCOPE210 minnonepermitted
Money movementSCOPE210 minnoneno
Comms blastSCOPE215 minnoneno
Infrastructure mutationANY215 min10 minpermitted

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.

06 / 08After it lands

The proof has a second life.

Having demonstrated the inverse once, the system can offer something almost nothing else can: a one-press undo on a production database, for as long as it is willing to vouch for that demonstration.
01

Health check

Production is re-checksummed the moment the change lands and compared against what the certificate predicted it would become.

02

Auto-rollback, where proven

A mismatch executes the inverse that was already demonstrated. Where it was never proven, AIRLOCK refuses and raises an alarm instead.

03

Thirty minutes to change your mind

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.

07 / 08The ledger

Break it yourself.

Decided changes are sealed into a hash chain, so editing the audit log is detectable by anyone holding an older copy of a single hash. Rewrite a record below and watch every link after it fail — the check runs in your browser, which is rather the point. A tamper check performed by the system holding the data proves considerably less than one performed by the person who does not trust it.
Sealed history
sealing…

    COMPUTING…

    Hashing three records into a chain with the Web Crypto API.

    Head

    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.

    08 / 08The index

    Four colours. Four meanings.

    Everything above is an argument that the state of a change should be legible at a glance. That is only true if the vocabulary is small and nothing decorative is allowed to borrow from it — so here is the whole of it, and the count of places each one may appear. The console you are about to open uses exactly these and no others.

    Proven

    PROVEN

    The change ran against a copy of the real rows and the rollback brought them back byte-identical.

    Certificates that passed. The one state in which the gate can open.

    --seal

    Sealed

    SEALED

    Refused. A proof that failed, a policy ceiling, drift, a missing signature, an injection.

    Every refusal, and the space where an approve control is not rendered.

    --fault

    Irreversible

    CANNOT BE UNDONE

    There is no inverse. An erasure, a refund, forty thousand emails.

    Scope certificates and armed approvals. Never hover, never emphasis, never brand.

    --hazard

    Interactive

    ACT

    Something you can operate: a link, a control, a focus ring.

    The only accent on this page. If it is this colour, you can press it.

    --ice

    347
    tests
    properties, not implementations
    23
    harness capabilities
    each lit only by a real event
    18
    fixtures
    seven distinct refusals
    0
    tools that write to production
    asserted in CI
    The whole of it

    Build the agent you would trust with root

    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.