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 says | What a request gets from it |
|---|---|
| A registration exists | nothing |
| The registration is linked to an agent we verify another way | an attribute on that agent, never a tier |
| The agent signed this request with its registered wallet | proven, 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:
| Parameter | Rule |
|---|---|
tag | erc8004 |
keyid | {agentRegistry}/{agentId}, the registration's CAIP-style key |
alg | must be absent; the tag pins the algorithm |
created | required; it is the replay bound |
| components | must 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 request | Tier |
|---|---|
| Recovers to the wallet the chain names | proven |
| Recovers to a different signer, and that wallet is a plain account | spoofed |
| Names a registration we hold no wallet for yet | claimed; the daily sync picks it up |
| Wallet is a contract, or we haven't confirmed which it is | claimed |
Signed for the www/apex sibling of the host it actually reached | claimed |
| Carries a body that couldn't be checked against the signature | claimed |
| Malformed profile, stale, or expired | unverified, 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.