‹ all posts

Five rounds before the agent signed anything

Erik Rekola · 2026-09-20

An agent moved tokens on Ethereum Sepolia after five fix rounds, each answering an independent review. Three attempts stopped before any signature.

The run was made on 15 September 2026 and the project is called Mandate Desk. An owner sets transfer caps, reviews the agent's exact plan, approves it for a limited window and can revoke the mandate. The code is public under MIT at erekola/mandate-desk. Substantial implementation code in it was written with AI tools, and the repository carries its own disclosure file saying so.

What does the owner actually approve?

Two hashes. One is the plan, meaning the recipient, the amount and the exact contents of the transaction. The other is the stable identity of the source code that will sign it.

The second hash is the one I did not start with. A plan hash on its own says nothing about the program holding the key, so a fix that changes one line of the signing path produces a program the owner never approved. The owner, the agent and the signer read that identity again before every call that can write. A process whose bytes differ from the approved identity still follows a broadcast transaction to its receipt by reads and records what it saw, but it never records a semantic result.

What stopped the first three attempts?

The parser that reads the preparation response, three times in a row, and nothing was signed in any of them.

The documented response carries an x402Requirements quote. My parser read that quote as a payment demand and stopped. I fixed it to read the quote as data, and the next attempt failed because the fix accepted it only as an object while the live response sent a list. The third attempt met an executionMode echo that is not in the documentation at all, and the exact key set refused it.

Four review rounds had not caught any of this, and the reason is worth naming. The fake gateway I tested against never returned a quote, so every test agreed with the parser about a field the parser had never seen. The parser now reads the quote in any JSON form, keeps it out of the transaction and out of the preparation hash and never pays against it. It admits the undocumented echo only when the client signed that value itself. The fake gateway sends both fields in every response now.

What did the run produce?

Six writes and four on-chain controls. Action setup, a token approval, the mandate grant, an agent execute of 10000 raw units through execute_approved, the revocation and an allowance reset.

Every write was confirmed with the same block hash on two independent read sources before a dependent write was prepared. The evidence package sits in the repository with the transaction hashes and explorer links, and it holds no signed bytes and no key.

Why was the first evidence package marked incomplete?

Because one of the two read sources had stopped reporting new finalized blocks the day before the run.

The completeness rule is the run's own. A package counts as complete only when both sources report every block of the run as finalized. Here one source finalized normally and the other was stuck on a block from the previous afternoon, more than twenty hours old. Calling the run finished on the source that worked would have been the easy reading, and it would have changed what the package means without saying so. The first export went out marked incomplete instead, and it was replaced when the stuck source recovered at 12:12 UTC, under four hours after the last write. The verification record names a deviation of the run itself. The last step was reached with a fetch throttle preload in the owner process, and that preload sits outside the approved code identity.

What does this not establish?

That the design is safe. One run on a test network says the path worked once under the conditions of that day, and the snapshot that made it passed its own suite of 390 tests twice.

It says nothing about an adversarial counterparty, a reorg deeper than the tests model, a signer running on a machine I do not control, or the same code against a different API than the one it was built for. Every review round so far has found something, which is the part I would weigh most if I were reading this from the outside.

Where the project is

The submission is on Brickken's developer hub under agentic tools: Mandate Desk on the Build with Brickken hub. The page has a vote button, one vote per person, and it does not ask anyone to create an account.

Frequently asked

Did the agent hold the key?

No. Signing happens in a separate signer process that the owner starts. The agent can prepare a request and call execute_approved for the one step the owner approved. No MCP tool exposes owner approval, the grant, the revocation, a signer or a key.

Is this a product?

No. It is a local reference implementation with a public repository and a demo you run on your own machine. There is no hosted deployment and no public demo address.

Did the run move real money?

No. It ran on Ethereum Sepolia, which is a test network, with test funds. The Sepolia fixture in the public repository pays a synthetic recipient address.

Why does an agent readiness business build a payment demo?

Because the interesting part of agent readiness is not the file you publish. It is the envelope an agent acts inside: what it may do, and what proof is left once it has done it. A transfer is the clearest case of that question I have found.