Docs
What keel is and how to use it.
keel decides whether a new version of your software is allowed to replace the running one. You import it into the deploy you already have. It does not deploy, run servers, or hold your keys. You supply those steps; keel makes the yes-or-no call and records it.
Quick start
Four commands, no account.
The quick path exercises the real library. It does not call a network service or ask for a secret.
git clone https://github.com/acoyfellow/keelclone the repobun installinstall, zero runtime dependenciesbun test75 deterministic checks across the gatebun run examples/refuse-bad-self-update/run.tswatch a bad candidate get refused
Concepts
The five parts.
A new version is named by its content. These five parts decide whether it is allowed to replace the one running now.
- Proof
- An Ed25519, artifact-bound record of what the verifier observed. A proof is admitted only for the exact content digest it names.
- Keyring
- Verifier keys with validity windows, rotation, and revocation. A valid signature from a revoked or rotated-out key is refused.
- Threshold
- k-of-n distinct trusted keys must sign. One compromised key cannot admit a candidate alone.
- Promote
- Compare-and-swap against the known baseline. A candidate cannot clobber production if the baseline moved underneath it.
- Decision
- Every step is hash-chained and signed, survives a restart, and an edited or removed entry is detectable.
Examples
What each example proves.
Every example runs on its own and writes a short receipt: what it checked, and where it could still be wrong.
- refused
Bad live candidate refused
A candidate that builds but fails a real request cannot promote.
bun run examples/refuse-bad-self-update/run.tspromotion denied; known-good restored - refused
Stolen token inert
A valid write token cannot promote a different artifact without a fresh proof.
bun run examples/stolen-token-useless/run.tsref unchanged after replay and forgery attempts - refused
One key cannot ship
A 2-of-3 threshold blocks a single compromised verifier key.
bun run examples/one-key-cant-ship/run.tssingle signer refused; edited log detected - accepted
Audit survives restart
Trust state and signed decisions survive reload and detect tamper.
bun run examples/audit-survives-restart/run.ts7 checks pass after reload and tamper injection - accepted
Hand-rolled gate deleted
A toy worker deletes local deploy-gate logic by importing keel.
bun run examples/delete-the-handrolled-gate/run.ts41 owner lines shrink to 25 while attacks flip to refused
Deploy
It is a library you import.
Import keel where your deploy already runs and give it four steps: deploy, verify, roll back, and store. keel makes the call; your steps do the work. There is nothing to host.
import { verifySignedProof, Keyring } from './keel/src/index.ts'path import; keel is not published to npmbun testkeel ships 75 checks; test your own ports the same way
Limits
What keel does not do.
These are the known edges. The default runner and store are starting points to replace, and the gaps below are written down on purpose.
- Live smoke
- The default runner boots a built server and sends one request. It is a reference runner, not a hosted preview environment.
- Minting
- The minting role and scoped token are enforced in the model. Production must back the minting role with a real, separately held credential.
- Persistence
- The default store is file-backed with a checksum. A KV, Durable Object, or D1 adapter implements the same interface.
- Trust root
- Threshold raises the cost of a single compromise. It does not remove the owner root as ultimate authority.
- Leverage
- The delete-the-gate result is real inside this repo. An out-of-tree worker doing the same against its live deploy is recorded as unproven.