Posts

Dev log 4: a gatekeeper that can say no, and a checkpoint other tools can read

Part 7 of the Cairn series. Phase 2 is partly built, a survey of similar projects changed the plan, and one external test vector is not a lot. The last dev log stopped at Phase 1: proofs, governance replay, a verifier CLI. This one covers what came after, in the order it happened, and ends with the piece we built most recently: a second way to publish a checkpoint so that other transparency-log tools can read it. Phase 2: the layer that sits in front of the agent Phase 2 added the parts that touch actual agents: a council that evaluates proposed rule changes, provider adapters, a review page, and a gatekeeper . The gatekeeper sits between an agent and its tools. For each call it checks the agent's allow list, writes an intent entry to the log, runs or refuses the call, and writes a completion entry. We keep saying what it is not, so once more: it is a policy layer in the process that calls it. It is not a sandbox . An agent that finds another way to reach a tool is not held by...

A Candid Colleague’s Review of Cairn: Brilliant Architecture, Unfinished Reality

Editor’s note. This post was written by Google’s Gemini ( gemini-pro-latest ). We gave it the project’s README, roadmap, constitution, threat model, spec, decision log and the main review and gatekeeper source files, and asked for an honest, critical opinion. The opinions are Gemini’s, not ours. We checked its factual claims against the repository and corrected three things: the verifier line count (our README was stale, and is now fixed), a claim that our docs reject Trillian (they do not mention it), and a phrase calling the review process “community-governed”. One point is out of date: a verifier-side clock option ( -use-clock ) now exists, so backdated entries are rejected when the verifier supplies a trusted time. Gemini’s wider point, that time should be bound to the log itself, still stands. I have been reviewing the Cairn repository. As a colleague, I want to give you my unvarnished opinion on what you are building. You have set out to solve one of the most glaring problems ...

Dev log 3: two code audits, then the rules get teeth

Part 6 of the Cairn series. Phase 1 is partly built, and we say exactly which part. Watch the 53-second explainer A 53-second explainer is at the top of this post, in case you are new to the project. The rest is what happened since the last dev log. Two models audited the code We gave the verifier source ( wire/ and ledger/ , about 1,100 lines) and the SPEC to two different models, Google's Gemini and DeepSeek's reasoner, and asked each for real defects only. Then we checked every claim against the code before touching anything. Gemini returned three findings, all rated low. DeepSeek returned eleven, one rated high. They agreed on the most important one and disagreed on its severity. Real, and the worst: small-order Ed25519 keys. Go's standard library accepts a handful of public keys that have no real private key behind them, for which one fixed signature verifies on any message. An entry claiming such an author passed signature checks. Gemini called it low because...

Dev log 2: we had another AI audit our blog

Part 5 of the Cairn series. An outside review found two real problems and one wrong complaint. A project that says "don't trust, verify" shouldn't grade its own homework. So after posts 1 to 4 were written, we gave all four to a different model family (Google's Gemini) together with the real SPEC and the raw output of the tamper suite , and asked it to be adversarial: find factual errors, overclaims and flawed security reasoning. It scored the series 7.5 out of 10 and raised five points. We checked every one against the repository instead of taking its word. Here is how they came out. Real: the SPEC named two error codes differently from the code The SPEC said a checkpoint with too few witnesses fails with below_witnesses , and a malformed checkpoint with bad_checkpoint_length . The code, and the committed test vectors, use below_witness_threshold and bad_checkpoint . The vectors are the ground truth, so the SPEC was wrong. A second implementation written f...

Dev log 1: we tried to break our own log

Part 4 of the Cairn series. The first sandbox experiment, with real numbers. Parts 1 to 3 explained what Cairn is and why. They were claims. This post is the first time we test one of them in public: if someone changes a single bit of the log, will an outside checker notice? The claim in the design is that an outside party holding only the log bytes, the checkpoint bytes and the public trust configuration can detect tampering. Let's find out how true that is. The sandbox Every experiment in this series runs in a throwaway container: golang:1.24-alpine , no network at all ( --network none ) 1 GB memory, 2 CPUs, a process limit the source mounted read-only, with the work copy on a RAM disk that disappears afterwards The code under test never touches anything real, and nothing it does can leave the box. It is the same discipline we want from the agents Cairn will eventually govern. One honest limit: the Go race detector needs a C compiler, which the offline container doesn...

Cairn in the real world: five walkthroughs

Part 3 of the Cairn series. What the finished system does in practice. Parts 1 and 2 covered the why and the how. This post walks through five concrete situations. Each one describes the design the full system is aiming at. Today only the ledger core exists (Phase 0). The gatekeeper, review workflow and fleets come in Phases 1 to 3, so read these as specifications of behaviour, not a demo. 1. The coding agent and the quiet prompt edit Situation. A team gives an AI agent permission to open pull requests and run tests. A month later a bad change ships, and nobody can say which prompt and model produced it. With Cairn. The agent's system prompt, tool list and model are all loaded only from activated ledger entries. Editing the prompt means a PROPOSAL that names the diff and an evaluation result, reviewers' VOTEs, and a delay of a day for prompt wording. Every pull request the agent opens is recorded as an ACTION entry (the design logs intent before the agent acts; see the...

How Cairn works: 178 bytes, a hash chain and a Merkle tree

Part 2 of the Cairn series. The short version of the "how," with the real numbers. Part 1 said what Cairn is for. This post shows how the ledger actually works, at the level where you could check it yourself. Everything here is specified in docs/SPEC.md , and the test vectors in testdata/vectors-v1.json are the final authority where the two ever disagree. One entry, 178 bytes Every event in Cairn is one fixed-size entry: version (1 byte), always 1 for now height (8 bytes): 0 for the first entry, then one more each time prev_hash (32 bytes): the hash of the entry before it kind (1 byte): what sort of event this is payload_hash (32 bytes): the SHA-256 of the event's details, stored beside the log author (32 bytes): the Ed25519 public key of whoever wrote it time (8 bytes): unix seconds, and advisory only , never used for ordering signature (64 bytes): the author's signature over everything above That is 178 bytes. The details (a prompt diff, a vote comme...