What Happened in There? A Tamper-Evident Audit Trail for AI Agents

decodebytes1 pts1 comments

What Really Happened In There? A Tamper-Evident Audit Trail for AI Agents | nono<br>The problem with "trust me, bro" logs

If you run an autonomous AI agent on your machine, you are giving a language model permission to open files, run commands, touch your filesystem, and reach out to the network. You know it's dangerous, but you have to trust it to do the right thing. You have to trust it to tell you the truth about what it did, and quite often they are outright liars.

So: what actually happened during that session?

Most tooling hands you a log file. A log file is a story the program tells about itself. If the program is compromised — or if the agent has managed to write somewhere it shouldn't — the log becomes part of the attack surface. "I didn't run rm -rf $HOME" is not evidence when the same process that might have touched rm -rf $HOME is the one writing the log entry that says so.

An audit trail for an untrusted process has to survive a very specific adversary: the process it is auditing . That means:

The audit writer cannot be the audited process.

The record has to be structured so that after-the-fact edits are detectable, not just discouraged.

The record has to be bound to the binary that actually ran, not just "a command called node".

A third party — not the host that produced the log — has to be able to verify all of the above.

This post describes how nono does each of these, walks through the crypto well enough that "Merkle tree" and "inclusion proof" finally make sense, and shows why the combination makes nono the strongest AI audit system for agent execution today, anywhere on planet Earth (the last bit is a bold claim, but I apologise, I'm a bit biased, I promise to be objective in my analysis from now on.)

nono's core architecture in one minute

nono runs untrusted agent commands inside an OS-enforced sandbox (Landlock on Linux, Seatbelt on macOS). The defining structural boundary for audit operations is two processes :

A supervisor (the parent) — trusted, unsandboxed, owns policy and auditing.

A child — the untrusted agent, fully sandboxed before exec.

The kernel mediates every boundary crossing between them. Capability requests (e.g. "the agent tried to openat(/etc/shadow)") are trapped by a seccomp BPF filter and delivered to the supervisor as seccomp-notify events. The supervisor decides; the kernel enforces.

The audit trail lives entirely in the supervisor. The sandboxed child does not write its own audit log. It cannot open the audit file, it cannot ptrace the supervisor, and it has no shared memory with it. The child generates events by doing things; the supervisor is the sole recorder of what happened.

A five-minute crash course in Merkle trees

Before we get to what nono does with them, a short detour on the data structure that does all the work.

Hashes, but structured

A cryptographic hash (SHA-256 is what nono uses) takes arbitrary bytes and returns a 32-byte fingerprint. Two properties matter:

Change even one bit of input, the hash changes unpredictably.

You cannot (in practice) construct two different inputs that hash to the same output.

bash

printf "hello world" | shasum -a 256<br>b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9<br>printf "hello evil" | shasum -a 256<br>b5e70ecd9dc3fd9459eaaf2adb3a51644351b76114f5f7bd8438d0ea6c53d481

A single hash is good for proving "this exact blob was not modified." But we have many events — thousands of capability decisions per session. Hashing them all into one blob means to prove one event happened, you have to re-hand-over every event.

A Merkle tree fixes this. You:

Hash each event individually — these are the leaves .

Pair the leaves and hash each pair together — you get the next level up.

Keep pairing and hashing until you end up with a single hash at the top: the Merkle root .

The root is one 32-byte value that cryptographically commits to every leaf underneath it. Change any leaf and the root changes. Truncate the tree and the root changes. Reorder the leaves and the root changes. Change one bit in any artifact and the root changes.

p.s. If you have never read the Bitcoin whitepaper, you should, don't be put off by crypto bros and math. It is a masterpiece of clear writing, brilliant design and engineering. The Merkle tree section is only a few paragraphs but it is worth the whole read.

p.p.s I am in fact Satoshi Nakamoto , you learned it here. I just don't have any proof (boom-tiss).

Inclusion proofs: one value, no full replay

Here is the quiet superpower of the trees. If I give you the Merkle root and then later claim "event #3 really was in the log" — I don't have to show you the whole log to prove it. I just have to show you:

Event #3 itself

The sibling hash at each level going up to the root (a handful of 32-byte values — log₂ N of them)

You re-hash upward. If you land on the same root you already know, event #3 was definitely in the tree. Nothing else about the tree had to be revealed.

This...

audit hash root nono agent merkle

Related Articles