D3 Edge

ERC-8004 agents

What an on-chain registration proves, what it can't, and how an agent signs a request.

ERC-8004 ("Trustless Agents") is an on-chain agent registry. An agent registers in a per-chain Identity Registry, gets an agentId, and points it at a registration file describing itself: name, services, trust models. The registration is public, durable, and checkable, away from the request; the file it points to is off-chain and can change.

The standard defines nothing that rides an HTTP request. No header, no signature, nothing bound to the request in front of you. Registration is therefore not evidence that this request came from that agent: the registry is public, so wearing a registered name costs an impersonator nothing.

What the chain saysWhat a request gets from it
A registration existsnothing
The registration is linked to an agent we verify another wayan attribute on that agent, never a tier
The agent signed this request with its registered walletproven, from the signature

So no tier ever comes from the registry itself. We mirror the Identity Registry on Ethereum mainnet daily over a read-only RPC and re-read agents' registration files on a rolling hourly schedule, each within about a day, and none of it runs on a request. Linking a registration to an agent we already recognise is a curation step on our side: it says "this identity is registered on-chain", and it never gives the agent a new way to be matched in your traffic.

Proving a request

An ERC-8004 agent can reach proven by signing the request with the wallet its own registration names on-chain. The chain already binds that wallet to the registration, with the wallet's consent; the signature adds the half the standard leaves out: that this wallet signed this request, for this host, just now.

The envelope is RFC 9421 HTTP message signatures, the same shape Web Bot Auth uses, with a profile tag that routes it to the wallet check:

ParameterRule
tagerc8004
keyid{agentRegistry}/{agentId}, the registration's CAIP-style key
algmust be absent; the tag pins the algorithm
createdrequired; it is the replay bound
componentsmust cover @authority; add content-digest when the request carries a body

The signature value is the wallet's EIP-191 personal_sign over the RFC 9421 signature base (signMessage in viem or ethers), so there is no custom crypto to write. On the wire:

Signature-Input: sig1=("@authority");created=1756800000;expires=1756800300;
                 keyid="eip155:1:0x8004a169fb4a3325136eb29fa0ceb6d2e539a432/42";tag="erc8004"
Signature: sig1=:BASE64(personal_sign(signature base)):

Because the host and the freshness window are inside the signed bytes, a captured signature can't be replayed at another site, and stops working once its window closes (five minutes by default). Because the keyid is signed too, a wallet bound to several registrations proves only the one it named.

What each outcome classifies as

The requestTier
Recovers to the wallet the chain namesproven
Recovers to a different signer, and that wallet is a plain accountspoofed
Names a registration we hold no wallet for yetclaimed; the daily sync picks it up
Wallet is a contract, or we haven't confirmed which it isclaimed
Signed for the www/apex sibling of the host it actually reachedclaimed
Carries a body that couldn't be checked against the signatureclaimed
Malformed profile, stale, or expiredunverified, with the reason

A proven request is logged as the agent we've linked the registration to, or, when there's no link, as the keyid itself, which proves the wallet and nothing more about who owns it.

If you run a site

Nothing to configure and nothing to maintain. An agent that signs this way lands in your log at the tier its signature earned, the same as any other proof, and one that doesn't sign is classified by everything else exactly as before. The log shows tier and identity today; there is no ERC-8004 badge in the dashboard yet.

On this page