The Setup
TEE attestation proves a code measurement. It does not create unique identity. Two TEEs running the same code produce identical attestations, so attestation alone cannot answer the question: which specific agent is this?
Ritual’s current answer is to use the owner’s Ethereum address as the agent’s identity. In practice, the platform treats ownerAddress as the stable input to HKDF and derives agent keys from that owner-rooted value. This works for the simplest model: one owner, one agent, one derived key namespace.
The problem is that this collapses two different concepts into one field. Identity is the thing being identified. Ownership is the control relationship over that thing. Today, ownerAddress -> HKDF -> keys makes ownership double as identity.
Two Concepts, One Field
Identity means: this agent is THIS agent. It is the unique identifier the rest of the system can use to bind keys, authorization, revival, execution, and audit history to a single agent instance.
Ownership means: this agent belongs to THIS owner. It is a control relationship. Owners can configure, recall, transfer, or authorize actions over an agent, but those actions should not redefine the agent’s cryptographic identity.
The current system makes ownerAddress both at once. That creates predictable symptoms: same-owner agents are cryptographically identical, transfer would break keys, revival depends on owner address, executors become over-authorized, and race conditions become possible between a displaced executor and a newly assigned executor.
ownerAddress carries identity and ownership at once. Right: each concept becomes a separate, addressable layer.Every Fix Rediscovers Identity
Earlier counter-proposals, including UUID indices and owner-plus-index disambiguators, arrive at the same destination by a longer route. To make a UUID index actually correct, the system needs per-agent auth, per-agent authorization, an on-chain index registry, and identity-aware revival. At that point the index is identity. It is just bolted on after the rest of the stack already assumed ownerAddress was enough.
Spencer’s P1+P2+P3 set identifies the same core problem in code. It introduces agentId and separates concepts that should have been separate from the beginning. The patches are not missing the issue; they are concrete evidence that the issue is real.
The concern is where identity lives. In the patch set, agentId becomes a retrofitted parameter threaded through PrecompileConsumer, AgentHeartbeat.sol, DKMS server.go, executor tokens, the reth tracker, and revival calldata. That can be made correct, but correctness now depends on every surface remembering the same identity boundary.
agentId. The primitive exists everywhere except where it should: at the bottom of the stack.What the PRs Actually Buy
P1 — Multiple agents per EOA.
Today, to run more than one agent from a single EOA, you deploy a separate contract per agent. That workaround exists because the platform uses the owner address as the agent’s identity, so two agents with the same owner collapse onto the same identity.
P1 makes the workaround unnecessary. A chain-issued agentId means one EOA can spawn N distinct agents directly. This is a UX unblock. It is not, by itself, a security property.
P2 — Refactor that closes a critical bug class.
P2 mostly refactors DKMS key derivation so it roots on agentId instead of ownerAddress. Concretely, it closes a real bug: Agent A can request keys from DKMS for Agent B if both agents share an owner.
“Agent A can request keys from the DKMS for Agent B if A and B have the same owner.”
That bug is patchable without the refactor. We could add a check that the requested derivation index belongs to the calling container. But that approach layers more defensive logic on top of an already confusing model. Re-rooting derivation on agentId removes the conditions under which this bug class exists.
P3 — One executor per agent at a time.
P3 enforces that any given agent runs on exactly one executor. After a revival or handover, the previous executor can no longer claim to act for the agent.
This has two practical effects. First, it removes the race condition where two executors believe they hold the same agent and both attempt on-chain actions, sign conflicting state, or race on nonces. Second, it restricts DKMS key requests to the currently bound executor. P3 is the only PR in the set that changes the safety model, not just the architecture.
The Cost of the Patch Approach
The patch set now has multiple places that independently encode “this is agent X”: agentId in PrecompileConsumer, agentId in AgentHeartbeat.SlotAssignment, agentId in DKMS HKDF info, agentId in the executor HMAC token, agentId in the heartbeat tracker in reth, and agentId in revival calldata.
Each one is a separately maintained encoding. Across 17 PRs and 10 repos, the system is correct only if every layer encodes the same identity boundary the same way. That is the cost curve. The first patch is cheap. The tenth patch is coordination risk.
The auditability cost is also real. “Who is this agent?” should have one canonical answer. Today, that answer is reconstructed from several local answers. The UX cost follows from the same root: users interact with ownership, but the system uses ownership as identity and as the derivation root. That is why two-agents-same-owner needs extra config. That is why revival cares about owner address. The user-facing concept and the cryptographic concept are the same field.
There is also an ecosystem cost. Nobody fuses identity and ownership this way. That means no precedent and no battle-tested patterns. Every future integration has to learn our local semantics before it can reason about agent identity correctly.
What Doing It Right Looks Like
Identity comes first. It should be chain-issued, unique, and independent of ownership. The identity should remain stable when ownership changes, when executors change, and when the agent is revived.
Ownership is then a relationship defined on top of identity. It is a control plane. That control plane can evolve to support transfer, revoke, grant, config, delegation, revenue routing, or lineage without changing the identity that DKMS and executor authorization consume.
Key derivation anchors to identity, not owner. AgentIdentityRegistry.sol is the right kind of primitive if it becomes the canonical source for agent identity instead of another registry that patch surfaces must remember to consult. The spec proposes ERC-721 as the concrete vehicle because it gives uniqueness, ownership, transferability, and ecosystem familiarity in one primitive. The core argument is not “use NFTs.” The core argument is that identity must be foundational.
The Honest Framing
The patches are not wrong. They work. They close real correctness gaps: P2 removes the same-owner key request bug class, and P3 removes the race where a displaced executor can still act as if it holds the agent.
The argument is not “this is broken.” The argument is that the cost curve gets steeper as the system grows. We are paying coordination cost across the stack instead of paying abstraction cost once in the core.
The choice is not patches versus no patches. The choice is whether to keep extending this pattern or invest in identity as a first-class primitive now, before agents handle real funds and the derivation anchor becomes load-bearing.