Verification

Signed Provenance for AI Coding Agents: How to Prove a Diff Wasn't Hallucinated

Akshay Sarode 8 min read
Direct answer

Signed provenance is a hash chain built from every stage of an agent run (the prompt, the exact commands executed, the resulting diff) that gets cryptographically signed with a key the agent process itself can't touch. Anyone downstream can check that signature against the published chain and confirm the diff is exactly what that run produced, not something hand-edited afterward or described by a summary that doesn't match reality. It proves origin, not correctness: a signed diff can still be a bad diff, it just can't be a diff that didn't actually come from the run someone claims it came from.

A teammate opens a PR and says an agent generated it overnight. CI is green, the diff looks reasonable, and you approve it in ninety seconds because that's what a green checkmark buys you. Six weeks later someone asks how a specific line got there, whether the agent actually ran the migration script it claims to have run, or whether a person quietly touched up the diff before it was pushed. There's no way to answer that. The commit history says nothing about it, the CI log says nothing about it, and the agent's own summary of what it did isn't evidence of anything. It's just more text the agent generated.

Tests passing doesn't tell you where a diff came from

"It compiles" and "the tests pass" are statements about the artifact, not about its history. They tell you the code runs and satisfies the assertions you happened to write. They tell you nothing about whether the diff in front of you is the actual, unedited output of the agent run someone is attributing it to, or whether the agent's stated reasoning matches what it actually executed to produce it.

Those are three separate failure modes, and green CI catches none of them. A hallucinated diff is one where the agent's summary describes work the diff doesn't actually reflect. A tampered diff is one where someone edits the code by hand after the run finished, so the pushed artifact no longer matches what the agent produced. A reasoning-execution mismatch is one where the agent's explanation of what it did doesn't line up with the commands it actually ran. All three can produce a diff that compiles cleanly and passes every test you wrote. None of them show up as a failing check anywhere, because a typical CI pipeline is designed to verify behavior, not origin.

What signed provenance actually records

Signed provenance treats a coding agent run as a sequence of stages and computes a hash at each one: the prompt that went in, the commands the agent actually executed, and the diff that came out. Each hash depends on the real content of that stage, so changing anything, the wording of the prompt, one command in the sequence, one line of the diff, changes the hash that follows it. Chain those hashes together in order and you get a value that only comes out one way if you replay the exact same run with the exact same inputs.

The chain value itself isn't the interesting part. What matters is that it's reproducible from the real inputs of a real run, and effectively impossible to reproduce by making something up after the fact.

1 PROMPT instruction text hash 9f2e…c31a 2 EXECUTION commands run hash 4b7a…08d2 3 DIFF file changes hash e12f…9a44 4 SIGNATURE chain signed key outside agent VERIFIABLE
Each stage of a run is hashed in order, prompt through diff, then the full chain is signed with a key the agent process never touches. Hash values shown are illustrative.

Walking the chain, stage by stage

The four tabs below spell out what specifically gets captured and hashed at each stage of a run, from the instruction text down to the final signature.

Try it: walk the chain

The exact instruction text handed to the agent, verbatim, including any system prompt or task file it was given, gets hashed before the agent does anything. That hash becomes the first link in the chain. If someone later claims the agent was asked to do X but the recorded prompt hash doesn't correspond to a prompt that says X, the claim doesn't hold up.

Every command the agent actually executes in the sandbox, shell commands, file writes, test runs, package installs, gets recorded in sequence and hashed. This is the stage that catches the gap between what an agent says it did in its summary and what it actually ran. A summary is prose the agent generated after the fact. The execution hash is derived from the real command log, not from the prose describing it.

The resulting file changes, the actual diff that would land in a working tree or a PR, get hashed once execution finishes. This ties the diff to the specific execution sequence that produced it. Change even one line after the run and the diff hash no longer matches what's recorded in the chain.

The prompt, execution, and diff hashes get chained together in order, and the whole chain is signed with a key that lives outside the process running the agent, by design. Anyone downstream, a teammate, a compliance auditor, a future version of you, can take the published chain and independently verify the signature without re-running anything, and confirm the diff is exactly what that specific run produced.

The execution stage is the one that matters most in practice. An agent's own summary ("ran the test suite, fixed the failing case, updated the migration") is easy to generate and easy to trust by default. The execution hash isn't derived from that summary. It's derived from the actual command log the sandbox recorded while the agent worked. If the two don't line up, that's worth investigating regardless of how convincing the summary reads.

Why the signing key can't live inside the agent process

The reason the chain is worth trusting at all comes down to one design choice: the key that signs it has to live somewhere the agent process itself can't reach. If the same process running the agent could also sign the chain, a compromised or simply misbehaving agent could, in principle, produce a plausible-looking chain for work it never actually did and sign its own alibi. Keeping the signing key outside the agent's reach is what gives the signature meaning. It's an attestation from outside the thing being attested to, not the thing vouching for itself. This is stated as the design intent behind the chain, not a claim about any one specific cryptographic mechanism.

This is the specific problem Bernato's cli/ surface is built to solve. It's a workbench and a bernato CLI, Rust across 14 crates with a TypeScript layer coordinating runs, that does sandboxed execution, runs a verification harness against what the agent did, and produces the signed provenance chain for the run. That's the "verify" half of the product, distinct from the "run and supervise" half described below. It's also the whole point of how Bernato describes itself: run coding agents on machines you own, and prove the run was real. The proving part is what this chain is for.

What signed provenance doesn't prove

This is worth being precise about, because it's easy to overstate. A signature on the chain tells you the diff is authentic: that it's the real, unmodified output of the run it's attached to. It does not tell you the diff is correct, secure, well-designed, or safe to merge. An agent can run an authentic, fully verified, cryptographically signed session and still write a broken query, introduce a security hole, or solve the wrong problem entirely. Provenance answers "did this really happen the way it's claimed." It has nothing to say about "was it a good idea."

It's also worth keeping this distinct from the other verification-adjacent piece of Bernato, which lives on the runtime/ side rather than cli/. runtime/, a Go daemon plus a web dashboard and mobile app, is the fleet supervision layer: live PTY access, approvals, and capability-token sandboxing that limits what an agent is allowed to touch while it's running. Today it keeps a local activity log of privileged actions, spawns, capability grants, approvals, with a hash-chained log verified hourly against an open-source verifier library as the direction that's headed. That's a log of what a daemon allowed to happen across a fleet of agents over time. The provenance chain described above is narrower: it's about one run, from one prompt to one diff. Different questions, different mechanisms, worth not conflating.

Where it sits next to tests and code review

None of this replaces the checks a team already runs. It adds a layer underneath them, one that answers a question tests and review were never designed to answer.

LayerWhat it tells youWhat it can't tell you
Tests / CIThe code behaves as expected against the cases someone wroteWhether the diff came from the run someone claims, or was edited afterward
Code reviewWhether a human judges the logic, security, and design to be soundWhether the diff matches the agent's actual execution, or just looks plausible
Signed provenanceThe diff is exactly what a specific, recorded run produced, unmodifiedWhether that code is correct, secure, or a good design decision

Signed provenance won't tell a reviewer whether a diff is good. What it removes is one specific kind of uncertainty going into the review: whether the thing in front of them actually is what it claims to be. That frees the reviewer's judgment for the question that actually needs a human, which is whether it's the right change. Bernato ships this free, local-first, and under Apache-2.0, on the theory that proof of an agent's work should live on hardware you control, not in a vendor's log you have to trust.

FAQ

What exactly does "verify" check?

It checks chain-of-custody and authenticity: that the diff you're looking at is the exact, unmodified output of the specific run whose prompt and execution log it's chained to. It does not check whether the code is correct, secure, or good practice. That's a different question, and it's still yours to answer through testing and review.

Can a malicious or compromised agent forge its own signature?

The design intent is no. The key used to sign the chain is meant to live outside the process that runs the agent, so the agent itself never has access to it. A compromised agent process can still misbehave during execution, which is what capability-token sandboxing and approvals on the runtime side are meant to contain, but it shouldn't be able to reach the signing key to fabricate a chain after the fact.

How does this relate to git commit signing?

Related idea, narrower scope. A signed git commit proves who, or which key, committed a given tree state at a point in time. It says nothing about how that tree state was produced: whether a human wrote every line, an agent generated it, or someone spliced the two together. Signed provenance covers the whole run, the prompt in, the commands executed, and the diff out, chained together, so it proves a specific agent run genuinely produced that diff, not just that someone with a key pushed it.

Is this the same as code review?

No, and it isn't meant to replace it. Signed provenance answers "did this diff really come from this run," which is a question of authenticity. Code review answers "is this diff good," which is a judgment call about logic, security, and design. A team needs both. A cryptographically verified diff can still be a bad diff.