Zain Dana HarperEMET · external witness

If an agent touched it, you should be able to check what it did.

I couldn’t take an agent’s word for what it changed, so I built something that checks for itself. It reads the file straight off the disk and works out a fingerprint, a short code that is unique to those exact bytes. Move even one character and the whole fingerprint changes. Then it compares that to the fingerprint it saved earlier and tells you one of three things: MATCH (nothing moved), DRIFT (something moved), or UNVERIFIABLE (it couldn’t check, and says so plainly). There is no fourth answer that just means trust me. The code was built so that answer can’t exist. It stands outside the thing it’s checking, and it never speaks for that thing.

A witness isn’t a judge. All it does is say what it compared. Whether to trust the result is yours, not the tool’s.

emet, Hebrew for truth · 1.1.0 on PyPI · 35/35 conformance · four languages
Plate 01 A structure dissolving into sorted runs: what drift looks like before the witness names it.

It works out the fingerprint itself.

An AI model is always handling files: one it edits, a prompt it’s given, a view it shows you and calls a true copy of the original. EMET opens those files directly and works out the fingerprint again from scratch, using SHA-256 (a standard, widely trusted recipe for turning any file into one short, unique fingerprint). Then it compares that to the fingerprint it saved before. The answer comes only from running the same recipe over the same file, never from the system telling you it’s fine. It stands outside the thing it’s checking.

Knowing where a file came from is one thing. It still doesn’t tell you whether the copy the model is looking at right now is unchanged. That’s the gap this closes.

Three answers. None of them trusted.

There are exactly three answers it can give (MATCH, DRIFT, UNVERIFIABLE), and there will only ever be three. That’s not a promise written in a comment somewhere; the code is shaped so a fourth answer simply has nowhere to come from. It can never tell you to trust something, because it was never given any trust to hand out.

MATCH the file’s fingerprint still matches the one saved earlier · it means nothing has moved, not that the file is good
DRIFT the file, or the view shown to you, no longer matches its saved fingerprint · it just reports that something changed, and doesn’t decide whose fault that is
UNVERIFIABLE it couldn’t read the file, or had no saved fingerprint to compare against · so it says “I can’t tell” out loud, with a clear reason. It never guesses, and never just crashes
If there’s no DRIFT, the answer is either MATCH or UNVERIFIABLE. There is no path through the code that turns “no drift” into “trust it.”

Don’t take the answer on my word. Watch it flip.

A single character is enough to change everything. Check a file, add one character to it, then ask again. The same rules, written out in three different programming languages, all land on the same answer.

Live the verdict depends on the bytes, nothing else
$ python membrane.py verify demo_sample.txt
MATCH  demo_sample.txt   want=d02206abca2a…  got=d02206abca2a…   (exit 0)

# append one byte to the file, ask the same question
$ python membrane.py verify demo_sample.txt
DRIFT  demo_sample.txt   want=d02206abca2a…  got=b072d611645e…   (exit 1)

# one spec, four separate language implementations, CI on every push
membrane.py        →  CONFORMANCE 35/35 vectors pass
impl/rust/emet     →  CONFORMANCE 35/35 vectors pass
impl/js/emet.js    →  CONFORMANCE 35/35 vectors pass
impl/go/emet       →  CONFORMANCE 35/35 vectors pass

Change a single character in the file and the answer drops from MATCH to DRIFT. It reads the file itself, not anyone who promises it’s fine.

Captured live from the emet run · receipt format from adapters/proof_surface_receipt.py · 35/35 conformance vectors, four language implementations, CI each push. Honest limit: I wrote all four versions myself, so they can only earn one honest claim: that the written rules are clear enough to build from. What they still can’t claim is that someone with no connection to me rebuilt the tool from the spec and got the same answers, and that different-author version is something I still owe. See a sample proof-surface report →

One set of rules, four languages.

I wrote the same rules out four separate times, each in a different programming language (Python, Rust, Node.js, Go), and each built only from what comes with that language, nothing borrowed from the outside. All four pass the very same thirty-five test cases on every change.

Four versions agreeing is not the same as four people agreeing.

They all came from one person: me. So they earn one honest claim and not a single one more: the rules are clear enough that someone can build a working tool from them. What I still owe is the real test, a version built by someone with no tie to me, a different author and not just a fifth language. I say that here in plain words, instead of letting the number quietly imply more than it should.

One file, or a whole packet.

Checking a single file is the core. But an agent rarely produces just one file, it produces a packet: a report, the things it references, the record of what it did. So EMET can now witness the whole packet at once. You hand it a small manifest (a list that names every file in the packet next to the fingerprint recorded for it), and it re-derives each one, opening every file and working out its fingerprint again from scratch, then comparing that to the fingerprint written down. It is the same recipe as before, run over a list instead of a single name.

The answer stays the same three, and only these three. MATCH means every file in the packet re-derived to the fingerprint recorded for it. DRIFT means at least one recorded fingerprint no longer matches the file on disk. UNVERIFIABLE means a listed file is missing or unreadable, or the manifest itself is malformed, so the comparison can’t be made. A difference always wins over a can’t-check: if anything drifted, the packet reports DRIFT. There is still no fourth answer, and no way for the manifest to smuggle one in. If a word that sounds like permission shows up anywhere in the list, even in a filename, it is refused before any verdict is formed. The witness runs entirely inside EMET, and it never becomes an authority.

Bundle the same three answers, over a content-addressed packet
$ python adapters/proof_surface_receipt.py bundle bundle.json
# re-derives every file the manifest lists, against its recorded sha256
MATCH        files_total=7  files_rederived=7   # every file re-derived
DRIFT        one recorded digest no longer matches the file on disk
UNVERIFIABLE  a listed file is missing, or the manifest is malformed

This is the external check for a generated proof-packet: not “the tool that made it says it’s fine,” but every file re-derived from scratch against the fingerprint written down for it.

Bundle witness from adapters/proof_surface_receipt.py · manifest schema proof-surface-bundle/v0 · same closed lattice, same no-authority contract as the single-file core. It reads the manifest and the files, and speaks for neither.

Copy it. Run the tests. Try it on real work.

It’s small, it’s open for anyone to read, and it leans on almost nothing else. Copy it onto your own machine, run the tests, point it at something you actually care about. And the moment it lets you down, tell me. I would honestly rather hear where it breaks than be told it’s fine. That’s what shows me what to build next.

$ pip install emet                                    # the tool, zero dependencies
$ emet selftest                                     # re-derives its own hash; never says “trusted”
$ git clone https://github.com/HarperZ9/emet && cd emet
$ python conformance/run.py membrane.py
CONFORMANCE 35/35 vectors pass
PyPI: emet  ·  GitHub: HarperZ9/emet  ·  Email: [email protected]  ·  License: MPL-2.0  ·  ← the map  ·  the index