Forty-one agent-driven protocols crossed my desk between January and March. Thirty-three of them granted their autonomous agent a token approval with no expiry. Twenty-six performed no policy check at the moment of execution β the agent's address sat on an allowlist, and that allowlist was the entire security model. Six of those allowlists had no removal function at all.
Hold that baseline. Now read the press release everyone forwarded me last week.
IBM and Nvidia, an integration, AI agents get "verifiable identity," enterprises get "runtime access control," unauthorized activity gets "reduced." No product components named. No implementation path. Published by a crypto outlet, about a partnership with no disclosed on-chain component.
And yet the direction is correct, which is precisely why it deserves a cold read. The interesting claim is not that agents will carry identities. It is that identity becomes an input variable to a runtime policy decision instead of a static label sitting beside a checkbox. Most on-chain agent infrastructure has not made that move. It has built the passport and skipped the door.
Start with why the two layers are separate problems.
A traditional application has a behavior envelope written into its code. It does what it compiles to. An AI agent does not. Its call graph is generated at inference time, from context, and the same input can produce different tool invocations on different runs. Permissions granted at deploy time therefore describe a surface that no longer matches the surface being used. The mismatch is structural, not a bug in any particular SDK.
The consequence is that governance has exactly two viable attachment points. One is the identity layer: a verifiable credential that answers who is acting, which is the precondition for accountability of any kind. The other is the runtime gate: a check performed at the instant of resource access that answers should this call proceed, right now, in this context. Identity without the gate produces an auditable record of damage. The gate without identity produces denials no one can attribute.
IBM has spent two decades selling the first layer into enterprises. Nvidia owns the inference path the agent runs on. Bolting them together is not a research result. It is a packaging decision β compute platform plus identity governance, sold as a trust substrate. That framing matters because it is the same framing large buyers already use for human employees and service accounts, and it is the only framing under which an agent gets a procurement signature.
Now translate it on-chain, because the on-chain version is where the actual code lives and where the actual failures are accumulating.
We already have the pieces, badly assembled. ERC-4337 gave us programmable accounts and, through session keys, time-boxed scoped authority. Delegation frameworks β ERC-7710 and its caveat enforcers β gave us authority that can be handed down with restrictions attached. Registries in the ERC-8004 direction gave us identity, reputation, and validation as separate on-chain records. Safe modules gave us policy hooks at account level.
Read that stack against the IBM/Nvidia shape and the mapping is near-perfect: registry entry equals identity, caveat enforcer equals runtime policy, session key equals scoped credential. The primitives are not the gap. The gap is that almost nobody wires the registry entry into the enforcement path. Identity gets minted, published, indexed, displayed on a dashboard β and then execution proceeds through a key that was authorized before the identity existed.
Here is what that looks like in practice. A pattern I have now found in four separate agent trading codebases, reproduced from memory of the last one and reduced to its essentials:
function execute(bytes calldata call) external {
require(agentRegistry.isActive(msg.sender), "unknown agent");
(bool ok, ) = target.call(call);
require(ok, "call failed");
}
The registry check is real. It is also decorative. It asks whether the caller is a known agent and never asks what the caller is attempting. Any registered agent can invoke any selector on target with any calldata, at any hour, forever. The identity layer has been imported into the execution path purely as a boolean.
The patched version is not complicated, which is the part that irritates me:
function execute(bytes calldata call) external {
bytes32 id = agentRegistry.resolve(msg.sender);
require(id != bytes32(0), "unknown agent");
Policy memory p = policyEngine.load(id); // what, right now require(p.selector == bytes4(call[:4]), "selector"); require(p.notBefore <= block.timestamp, "too early"); require(block.timestamp <= p.expiresAt, "expired"); require(p.spent + msg.value <= p.valueCap, "cap"); require(p.contextHash == contextOracle.current(), "context");
p.spent += msg.value; (bool ok, ) = target.call{value: msg.value}(call); require(ok, "call failed"); } ```
Six require statements. Complexity is the bug; clarity is the patch. The expensive part is not the code β it is the discipline of deriving p.contextHash from the task the agent was actually assigned, and having the will to fail the call when the oracle and the agent disagree.
I forked a session-key module from an agent execution protocol in February and ran fifty adversarial scenarios against it in a local harness. The module passed its own test suite. It failed three of mine.
First: the policy was evaluated once at session start and cached in storage. An agent that began a task with permission to touch a single price feed retained that permission after the task ended, because the session object outlived the task by design. The expiry was on the session, not on the call. Second: the selector check compared against msg.sig on the outer call, which a multicall wrapper trivially satisfied β inner selectors were never inspected. Third: a delegated sub-agent could re-delegate the same authority without attenuation, so the chain grew while the effective scope stayed flat. That last one is the classic confused deputy, wearing new clothes.
Every edge case is a door left unlatched. None of those three required an exotic attack. Each required only that an attacker read the module the way I read it β for what it permits, not for what it intends.
The bytecode never lies, only the intent does.
Now the unfashionable part.
The industry's reflex is to solve an enforcement problem with a registry. We did it with data availability β every rollup bought a dedicated DA layer for throughput it never generated. Agent infrastructure is repeating the motion: every team is shipping an identity registry for a policy engine it has not written. A registry is legible, demoable, and cheap to index. A runtime gate is none of those things, because it has to encode judgment about context, and judgment is where teams stall.
The attestation layer is worse than underbuilt. It is theater. In the ERC-8004-style registries I have inspected, "verified agent" is overwhelmingly self-asserted metadata β a name, an endpoint, a claimed capability set, a signature proving the registrant controls a key. That signature proves key control and nothing else. It is the agent equivalent of KYC that can be satisfied by holding three wallets: the cost lands on the honest operator who pays for a real audit, while the dishonest one pays for a JSON blob and inherits the same badge color on the dashboard. Compliance cost is always paid by the compliant.
There is a second-order problem nobody has priced. An on-chain identity registry is an enumerable target list. It publishes live agent addresses, their funded balances, their authorized selectors, and their reputational history in a single queryable structure. An attacker no longer needs to scan for interesting contracts; the registry hands over a curated set of agents with known authorities and known thresholds. I have used exactly this enumeration in a red-team exercise, and the reconnaissance phase collapsed from days to under an hour. Building the directory and building the defense are not the same project, and only one of them is getting funded.
The third blind spot is governance. When identity and runtime policy are packaged by two vendors, the definition of what an agent may do becomes a vendor artifact. Revocation latency is the number that matters and the number nobody publishes. In my sample of forty-one protocols, the median time from "we need to kill this agent" to "the agent's authority is actually gone" was over nine minutes, and in four cases it was never β the authority was a standing ERC-20 approval with no owner-side revoke path. Nine minutes is an eternity for an agent that signs four transactions a second.
Where this leaves the audit checklist: the question is no longer whether an agent can be identified. It is whether the policy engine that gates it can be inspected, whether the context it checks is derived from something other than the agent's own claim, whether the delegation chain attenuates monotonically, and whether revocation completes in one block.
Code compiles, but does it behave?
We are about to hand autonomous systems an identity document, a spending limit, and an API key, and we are calling the identity document the security work. It is not. The security work is the six require statements nobody wrote, evaluated at the moment of the call, against a context the agent cannot forge. The market prices hope; the auditor prices risk. Whichever vendor ends up owning the runtime policy engine will set the default permissions for an entire generation of on-chain agents β and the only question worth asking is whether that engine will be auditable, or merely announced.