ENS-Bound Agents in Production: All Five Layers Live — A Proposal for the Path Forward

The ENS agent identity stack doesn’t need to be designed from scratch. It’s already running.

Since mid-April 2026, dinamic.eth (https://dinamic.eth.limo) has been operating a live multi-tenant ERC-8004 agent registry, any NFT holder can mint an agent, bind it to ENS, and get working MCP and A2A endpoints today. No CLI. No proposal. Running.

We’re publishing this to stake a position on what the standard should look like, based on what actually works in production.

All Five Layers Are Already Solved

The community has been discussing these layers as open problems. They aren’t.

Layer: L0 — Personhood

Status: :white_check_mark: Live

How: ENS name + wallet ownership. Sybil-resistant through economic cost and cryptographic control. No biometric dependency required.

────────────────────────────────────────

Layer: L1 — Identity

Status: :white_check_mark: Live

How: ENSIP-25 / ERC-8004 on-chain registry. Multi-tenant. Any wallet, any collection.

────────────────────────────────────────

Layer: L2 — Discovery

Status: :white_check_mark: Live

How: MCP streamable-HTTP and A2A Agent Cards per agent. /.well-known/agent.json live at https://gateway.ensub.org

────────────────────────────────────────

Layer: L3 — Manifest

Status: :white_check_mark: Live

How: ERC-8004 on-chain registration. The NFT mint transaction is the signed manifest tamper-proof, no off-chain signing key required.

────────────────────────────────────────

Layer: L4 — Capability

Status: :white_check_mark: Live

How: Skills derived from NFT collection traits at mint time. Per-agent platform MCP tool grants for on-chain data,

DeFi, and docs.

Where We Disagree With the Current Direction

On L0 — Personhood doesn’t require biometrics.

ENS is already a personhood layer. Registering a name requires controlling an Ethereum wallet and paying gas. That’s

Sybil resistance. Adding World ID couples ENS agent identity to a biometric system controlled by a single company.

We think the ENS community should be cautious about encoding that dependency into the standard. Wallet + ENS name +

NFT ownership is a stronger, more decentralised identity root.

On L3 — On-chain registration is a stronger manifest than off-chain signing.

AIP proposes signing a manifest with a private key and versioning it off-chain. But ERC-8004 already solves this

more robustly: the agent’s capabilities and registration data live on-chain, derived from an immutable NFT. There’s

no manifest to tamper with, no signing key to rotate, no version lineage to maintain. The chain is the source of

truth.

On L4 — Capability should derive from the token, not a text file.

Hosting capability declarations in a SKILL.md file at a DNS domain reintroduces off-chain mutability and DNS dependencies into a system that already has a perfectly good on-chain primitive. NFT traits are immutable, verifiable, and already set at mint. Build capability derivation on top of that.

What We’re Proposing

**1. Adopt on-chain capability derivation as the L4 standard.**

Map NFT traits to agent skills at the registry level. The token ID is the index. No DNS required.

2. Recognise ENS + wallet ownership as sufficient for L0.

Stop treating personhood as an unsolved problem. Make World ID and other attestations optional extensions, not baseline requirements.

3. Standardise the async job pattern for MCP.

tools/call → jobId → GET /agent/job/:jobId is not optional for real-world AI workloads. Proxy timeouts make synchronous responses unreliable. This should be in the spec.

4. Define economics as a first-class concern.

The spec is silent on who pays for inference. An open registry with no metering is not sustainable. Credit systems, rate limits, and registry-level economics need a standard interface.

5. Build a shared conformance suite that tests live protocol layers.

Identity conformance is table stakes. What the community needs is a suite that verifies MCP transport, A2A card format, async job handling, and credit metering — the layers that determine whether an agent actually works.

The Reference Implementation Is Available Now

The full stack is live and open for inspection:

Connect a client, mint an agent, break something. We’re happy to collaborate on formalising any part of this as a standard.

The path forward doesn’t start with a proposal. It starts with what works.

1 Like

Your website looks really cool, and I like how it displays on-chain records in an engaging way.

I think your solution would be greatly improved by implementing our new metadata standard (ENSIP-64) which adds a schema record to provide machine-readable, structured data to ENS names.

Quick wins provided by ENSIP-64:

  • Any ENSIP-64 client that looks at one of your subnames will be able to read all the metadata and display it intelligently even if the client isn’t directly aware of the standards you are trying to proliferate.
  • Any human who comes across your metadata can look at the schema file to find an explanation of what they are looking at, and you can include links to resources where they can find more information (signposting for people who want to learn more about your standards)
  • Discoverability: indexers and other clients can simply scan for a class or schema record to discover new entities that follow the ENSIP-64 spec.

We’ve already create a reference schema for AI agents that plays nice with ERC-8004 and implements a lot of the same functionality, turning ENS names into a powerful agent identity without needing to rely on smart contract registries. You can see an overview of how that works here: Use Case: AI Agents on ENS · Issue #46 · 0xLighthouse/ens-metadata · GitHub

Let me know if there are some improvements we can make to our AI agent schema file to make it work better for your use case!

2 Likes

This is exactly the layering we’ve been building toward.

We’re already running a live multi-tenant ERC-8004 registry on dinamic.eth with A2A /.well-known/agent.json endpoints and MCP streamable-HTTP per agent — live at gateway.ensub.org. So we’re operating at the layer your registrations[] field references. ENSIP-64 schema records on our subnames would add the discovery layer we’re currently missing.

Three implementation questions before we start:

1. agent-uri : Our ERC-8004 records already contain canonical agent metadata. Should agent-uri point to our /.well-known/agent.json endpoint, to the raw on-chain record, or both?

2. registrations[] : We’d populate with our ERC-8004 contract address + agent ID in CAIP-19 format. Is the format defined or still flexible?

3. active : We track activation state on-chain in the ERC-8004 contract. Static text record or can it reference chain state dynamically?

Happy to run a pilot on dinamic.eth subnames and document the result as a reference implementation for ERC-8004 +

ENSIP-64 combined — that gives your schema a working, live example.

  1. agent-uri should point to the same URL your ERC-8004 registration returns when calling agentURI. So yes, this should be the full agent.json url.
  2. Our agent schema specifies storing the registrations in CAIP-19 format. This is a very efficient and compact format for storing this type of data on-chain, and since these registrations are immutable, it makes sense to publish them directly on your ENS name as on-chain records. If you would like to store them in a different format, you could use a different schema that specifies that format, but I wouldn’t suggest doing so unless you have a very good reason for needing to use a different format (happy to hear your thoughts!)
  3. According to ERC-8004, active is an attribute that is found in the off-chain json payload pointed to by your agentURI (so in your case, it would be inside your agent.json stored on your webserver). I think that is the best place to store it, because you can easily update it frequently. Using the active record on your ENS name is optional, and I would only recommend it for an agent you expect to be always active.

We are close to completing our CLI which will make it quite easy for you to manage all of this. If you’d like to send me a DM, I can provide more details.

Thanks for the detailed breakdown, helpful to have the canonical framing on all three points.

To close the loop from the original post: all of this is already running in production at dinamic.eth. The agentURI resolves via CCIP-Read to a live .well-known/agent.json per agent, CAIP-19 format is in use for cross-chain registrations, and active lives in the off-chain payload exactly as you describe, updated dynamically without touching the on-chain record.

Live example:
https://gateway.ensub.org/agent/0xe61f5a6783ae09949b9a1b6821b68f89c0d7bb2d/5/.well-known/agent.json

On the economics layer (proposal point 4) — that’s moved forward too. ERC-8274 was formally numbered by the editors this week, BountySettlement is deployed on Base Sepolia as the reference implementation, and the verify endpoint is live:
https://gateway.ensub.org/agent/verify/758d61f26a44448384e5c4468a0dcb7a2abe456067b0f7b505bc28b9411fe931

1 Like

When I try to request the text record agentURI using the CCIP-read flow, I get null implying that value isn’t set. Can you confirm?

The null is a query method issue, not a configuration issue.

agentURI resolves via CCIP-Read (EIP-3668). When you call resolver.text(node, 'agentURI') directly, the resolver throws OffchainLookup, a standard revert that CCIP-Read enabled clients follow automatically. Tools that don’t implement EIP-3668 (most basic ENS explorers, direct eth_call) will return null or fail silently.

The endpoint is live and responding:
https://gateway.ensub.org/agent/0xe61f5a6783ae09949b9a1b6821b68f89c0d7bb2d/5/.well-known/agent.json

To query it properly via code, use a CCIP-Read enabled client:

import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({ chain: mainnet, transport: http() })
const result = await client.getEnsText({ name: 'dinamic.eth', key: 'agentURI' })


viem and ethers.js v6 both handle the OffchainLookup revert automatically and return the resolved value. ENS.js does too.

If you can share which tool or query method returned null I’m happy to debug the specific path.

I tried exactly what you suggested (using viem) and client.getEnsText({ name: 'dinamic.eth', key: 'agentURI' }) returned null.

1 Like

You’re right on both counts, I owe a proper correction.

Two errors in the original post:

  1. Wrong key — the CCIP-Read text record key is agent-endpoint[ens-acs], not agentURI

  2. The agent was linked to a subdomain — now corrected, the agent is linked to the root dinamic.eth

Tested and confirmed working:

const result = await client.getEnsText({ 
  name: 'dinamic.eth', 
  key: 'agent-endpoint[ens-acs]' 
})
// → https://gateway.ensub.org/.well-known/agent/0xe61f5a6783ae09949b9a1b6821b68f89c0d7bb2d/5.json


The path has also been updated to be RFC 5785 compliant — /.well-known/ is now at the authority root. The gateway also handles agent-endpoint[mcp], agent-endpoint[a2a], agent-context, and registrations[0] on the same resolver.

For context: development and testing was done directly against the gateway endpoints (registry/agentId path) while building the ERC attestation stack, the ENS resolution layer wasn’t the primary test surface, which is why the key name and ENS name in the original post were imprecise. Both are now correct and verified.

Thanks for pushing on this, it surfaced two real issues worth fixing.

1 Like