An agent that can sign is a principal
If an agent can call sign, it is a principal the brief has to name. The contract still thinks it is talking to a key.
2026-07-18 · blockchain + AI
Access control on a vault is usually a list of keys. Owner. Keeper. A role that can set a parameter. The contract does not know what those keys are attached to. It checks msg.sender or an ecrecover and moves on.
Teams still talk about the agent as a user of that contract. A helper. A CLI with extra steps. That language leaks into the brief, and then the brief never names the thing that can actually produce a signature.
If a process can call sign, it is a principal. Not a user. Not a convenience layer. A principal.
What the contract actually sees
Say the vault allows KEEPER_ROLE to write an LTV ceiling. A person used to hold that key. Then someone put the key in a process that also runs a model, because the model is the thing that “knows” when the ceiling should move.
The contract did not change. The invariant you wrote last year might still say “transfer preserves the sum of balances when the signer is a known key.” That sentence is now false in a way the compiler will not catch. The signer is known as a byte string. It is not known as a decision-maker.
This is the join. A blockchain review reads the role. An AI review reads the tool list. Each side can be internally tidy. The connecting fact is that the model and the key live in the same process, or that the model’s output is the only input the signer looks at.
I keep seeing the same sentence in design docs: “the agent proposes, a human approves.” Then I open the runtime and sign_tx is a tool the model can call. Proposal and approval collapsed into one hop. The human is in a Slack channel that nobody wired into the signer.
Users do not hold keys. Principals do.
A user is a person who asked for something. A principal is whoever can make the contract accept a call. Those are different sets. They overlap when a person holds the key. They stop overlapping the moment you give the key to a program that chooses its own arguments.
Calling the program a user hides that. It makes the threat model look like phishing or a leaked password. Those are real. They are not the interesting case. The interesting case is an authorized signer whose policy is a paragraph in a prompt.
Prompt policy is text. The contract cannot read it. The tool host will not enforce it unless you wrote that enforcement as code, with a deny that does not go back through the model. “Do not transfer more than X” in the system prompt is a wish. The function that holds the key does not consult the wish.
So write the agent down as a principal in the brief, with a capability list. What can it sign. What can it not sign, and what code makes that true. If the answer is “the model is instructed not to,” the brief should say the constraint is unenforced. Then you can decide whether that is acceptable. Pretending it is a user skips the decision.
What changes in the review
Once the agent is a named principal, a few questions stop being philosophical.
Does any path let the model both author the call and produce the signature? If yes, say so. That is the same class of finding as an owner key sitting in a frontend bundle, just with more steps.
Is the key in the same address space as the tool host? Then a prompt-injection in a fetched page is not “the model said something weird.” It is a confused principal. The contract will honor the signature.
Can a public channel write into a memory store the signer reads? Then the channel is on the capability list, whether anyone put it in the architecture diagram.
None of this requires a new primitive. It requires the brief to stop using “user” for a process that holds sign. We will ask for that list during scoping. If you do not have it yet, that is fine. Writing it is part of the work.
If the join is the part you want reviewed, start at Joins. Our methodology. Request an audit.
Next step
Request an audit
If this bug class applies to your system, share your repo, scope, and timeline. We’ll propose an audit plan and estimate.