← All articles Engineering

We don't trust the bytes our agent sends. We trust the hash.

OBTO Team · Insights from the Glass Box

An agent on our team tried to publish a blog post last month. Not a draft in a staging bucket — the actual page, written straight into the production database that serves this site. It assembled about twenty-six kilobytes of HTML, called the deploy tool, and the platform threw it back: sha256_mismatch. Somewhere in the middle of the file, a handful of bytes had arrived as something other than what the agent meant to send. No syntax error. No visible damage. The page would have rendered — slightly wrong, in a way nobody would catch until a reader did.

In most shops, the thing that catches that is a person reading the pull request. We don't have that step. So I want to walk through what we put in its place, because "an AI agent writes directly to production" is either the most reckless sentence in our architecture or the most deliberately engineered one, and which it turns out to be comes down to a couple of specific decisions.

What "writes to production" actually means here

Start with the shape of it. On OBTO, an agent doesn't open a branch, push a commit, and wait for CI to go green. It calls a tool that writes a record — a page, a route, a server script — into the live database, and that record is what production serves the next time someone loads the site. No git in the loop. No staging deploy. No second person.

Every safeguard a conventional pipeline gets for free — from the plain fact that a human typed the code and another human read the diff — we get none of by default. That's not a gap we discovered late. It's the premise. If you want an agent to ship on its own, you either build structural equivalents of review and CI, or you accept that "autonomous deploy" is a synonym for "hope." We were not interested in hope.

An agent is a bad courier for large files

The first thing you learn building this is that a language model is a wonderful author and a mediocre delivery truck. Push more than roughly twelve or thirteen kilobytes of content through a single tool call and you are trusting every layer between the model and the tool to preserve it byte-for-byte. Usually it does. Occasionally it doesn't: a character gets re-encoded, a chunk gets clipped, and the model has no way to notice, because from where it sits the text still looks like what it wrote.

For a sentence of prose, that's a typo. For a block of JSON-LD or a stylesheet, it's a broken page. And you cannot fix it by telling the agent to concentrate harder. It isn't a diligence problem. It's a transport problem, and transport problems want a transport-level answer.

So we stopped trusting the transport

The deploy path is content-addressed. Before it writes anything, the agent computes a SHA-256 of the exact bytes it intends to ship. The platform commits the write only if the bytes it assembles hash to that same value. One byte off, anywhere in the file, and the write is refused — nothing reaches production, and the agent gets told exactly why.

For anything large, we take the agent out of the byte path entirely. It uploads the file out-of-band, then hands the platform a URL and the promised fingerprint, and the server fetches the bytes itself:

// the file never streams through the agent's own context
obto_stage_chunk({ action: "from_url", url, expectedSha256 })
obto_upsert_record({ uploadId, sha256 })
//   -> committed only if bytes === promise, else sha256_mismatch

The agent's job shrinks to declaring intent: deploy the thing at this URL, and here is the fingerprint of what it should be. The platform's only job is to check the fingerprint. Neither side has to trust the other's good behavior, because a hash isn't a matter of trust. Two files have the same SHA-256 or they don't. There is no "close enough," and there is nothing to argue with.

The edit has a different failure, so it gets a different check

Whole-file deploys are one half of the problem. Edits are the other, and they fail in their own way: the file you think you're editing may not be the file that's actually live. An agent fetches a page, reasons about it for a while, and by the time its patch arrives the content may have shifted underneath it. The line it read as line 40 is now something else.

So patches are anchor-matched. When the agent edits a line, it also declares the first twenty or thirty characters of the line it believes is sitting there. If the live file has drifted, the anchor won't match, and the edit is refused before it can overwrite the wrong content. It's the hash idea applied to a scalpel instead of a whole page: the change lands only if the world is still shaped the way the writer assumed it was.

You can write a very convincing commit message. You cannot write a convincing SHA-256.

This is the review step, rebuilt out of structure

Put the two together and you have our stand-in for the human. In a normal pipeline, review and CI are what sit between "someone wrote this" and "production runs it." They work because a person read the diff and a machine ran the tests. Our replacement is structural rather than social: a full write commits only if its bytes match a hash the author committed to, and an edit commits only if the line it changes is the line the author actually saw. Both checks are indifferent to how persuasive the agent is. That's the whole point of leaning on math — it doesn't have opinions, and it can't be talked around. We built the same instinct into how writes stay exactly-once under a dropped network reply, and into why we deleted server-side session state so nothing can silently drift between calls.

What a hash does not buy you

Here is the part worth being plain about, because it's the easy thing to oversell. None of this makes the content good. A hash guarantees that the page we shipped is byte-for-byte the page the agent meant to ship. It says nothing about whether the agent meant to ship something sensible. Integrity is not correctness. The check that catches a corrupted deploy is simply not the check that catches a bad idea — that one is still taste, tests, and a human who cares, and we've never pretended a fingerprint substitutes for judgment.

What content-addressing actually buys is narrower and more valuable than it first sounds: it deletes a whole category of silent failure. When something is wrong now, it's wrong on purpose, out loud, where you can see it and argue with it — instead of wrong by accident, in the middle of a file, in a way that surfaces weeks later as a reader's confused email. That's the same reason we think the human should stay in charge of the judgment an agent can't fake in production, and let structure carry the parts that are mechanical.

The question to ask any platform

If you're weighing whether to let something autonomous near your production systems, this is the question I'd put to any platform, ours included: when the machine is wrong, what refuses the write? A vague answer means the safety was social — a review step, a person — and there is no person here at 3 a.m. On OBTO the answer is concrete: a hash and an anchor, and every one of those checks shows up in the Glass Receipt, so you can watch them fire.

The getting-started guide takes about ten minutes on the free Builder tier, and the pricing is flat and published. Describe it, ship it, own it — but verify the bytes first.

Watch the checks fire

The Glass Receipt: a per-run, per-step ledger of every write, hash, and refusal — by default.

Get started

More from the OBTO blog