Auditing the Agent Name Service: 351,912 log entries
The Agent Name Service promises a verifiable answer to "who is this agent?". We downloaded all 351,912 entries in its production transparency log and checked the answer against the spec. The cryptography holds up. What gets registered is another matter.
ANS is the most complete proposal so far for anchoring agent identity to the existing internet. It uses DNS names, ACME validation and X.509 certificates, plus an append-only log that anyone can audit. It is also the only one of these proposals with a large production deployment: GoDaddy runs a Registration Authority and a public Transparency Log, and ships open-source SDKs in Go, Rust and Java.
This post does three things. It reads the spec: the IETF draft and the seven layered specifications in the reference repository. It verifies the live log with nothing but a root key and SHA-256. And it takes a census of every event the log has ever sealed. The gap between what the protocol can prove and what the deployment actually records is the useful part.
What ANS is, in one paragraph
The current text is draft-narajala-courtney-ansv2-01, dated 13 April 2026. It is an Independent Submission with Informational status, expiring 15 October 2026. Its authors are from GoDaddy, OWASP, DistributedApps.ai and Cisco. It replaces the May 2025 draft-narajala-ans-00, which has expired. The core idea: every agent's identity is anchored to a domain name. A Registration Authority (RA) checks that you control the domain via ACME. It then issues two certificates and seals the event into a Transparency Log (TL). The draft sums up the split this way: "DNS-AID tells a client where to connect, ANS tells it whether to trust the agent."
The identifier is the ANSName, ans://v{major.minor.patch}.{fqdn}. For example, ans://v1.0.0.sentiment-analyzer.example.com. The version is part of the name on purpose. Every code or capability change requires a new version and a new registration. This targets a failure mode the draft names outright: a supplier passes an audit, then "quietly update[s] its model. The certificate stays valid, the endpoint stays up, but the code behind it is no longer what was audited."
Two certificates, three tiers
The dual-certificate model is the most interesting design choice. The Server Certificate is an ordinary public-CA certificate for the stable FQDN, and it survives version bumps. The Identity Certificate comes from a private CA and carries the versioned ANSName in a URI SAN. A private CA is required because CA/Browser Forum Baseline Requirements prohibit URI SANs in publicly trusted server certificates. So no public CA can issue an ans:// certificate.
Verification comes in three tiers, and they describe what the client checked, not a grade the RA assigns:
- Bronze: standard PKI validation of the server certificate.
- Silver: Bronze plus DANE, a
TLSA 3 0 1record at_443._tcp.{fqdn}whose SHA-256 matches the server certificate. DANE requires DNSSEC. Per RFC 6698, an insecure TLSA RRset is unusable. - Gold: Silver plus a Transparency Log check that the registration was sealed and that the certificate fingerprint matches the sealed one.
The draft is candid about what the RA does not do. It answers "Who are you?" and nothing else. Operational maturity (SOC 2, SBOMs) and behavioral reputation are pushed to separate layers. A companion Trust Index specification scores agents on five dimensions: integrity, identity, solvency, behavior and safety. Solvency is where payments enter. The Trust Index spec defines zero-knowledge solvency proofs over balances such as USDC, pinned to a chainId and a maxBlockAge. When an agent has an ERC-8004 registration with its own agent wallet (setAgentWallet), the proof should check that wallet, not the token owner.
The repository has moved past the draft
The reference repository now splits the protocol into seven specs, ANS-0 through ANS-6. Most were rewritten against the reference implementation in July 2026, and ANS-6 was added in September. Three changes matter for anyone building an agent runtime:
DNS-AID is the default discovery profile since 23 July. Registrations now emit one RFC 9460 SVCB record per endpoint, carrying DNS-AID parameters in private-use keys (key65400 for the capability URL, key65401 for its SHA-256). The _ans TXT record from the draft becomes opt-in.
An ENS identity profile landed on 20 August. It lets an RA accept a .eth name, with control proven by an EIP-712 or ERC-1271 signature from the owner account resolved on-chain. The profile notes the obvious risk: ENS names transfer and expire, so monitoring carries more weight than for any other kind of identity.
ANS-6, agent-to-agent authentication, merged on 14 September. This is the per-request layer, and it is well engineered. It requires three independent proofs: identity (the certificate is sealed in the log), liveness (the registration is valid now) and possession (the peer holds the private key for this request). As the spec puts it, receipt verification without a liveness check "authenticates a corpse."
ANS-6 defines two ways for a caller to prove possession. Method A is mTLS with the Identity Certificate. Method B is an RFC 9449 DPoP proof in an HTTP header. Method B exists because mTLS does not survive L7 proxies that terminate TLS, which describes most production gateways. The profile is strict: the JOSE header must be exactly typ, alg (ES256 only), jwk and a single-entry x5c. A mandatory ans_content_digest claim binds the request body. Any extra header parameter rejects the proof.
The recommended "SCITT tier" needs no network calls per request. Each agent attaches its own COSE receipt and a short-lived status token as HTTP headers (X-SCITT-Receipt, X-ANS-Status-Token). The verifier checks both locally against a root key it fetched once. Revocation is bounded, not instant. At default settings the worst case is one hour of token TTL, plus 10 minutes of clock skew, plus up to one TTL of grace: 2 hours 10 minutes.
Verifying the live log from scratch
The production log is at transparency.ans.godaddy.com. Every read endpoint is unauthenticated, as ANS-4 requires. We fetched the root key, then the C2SP checkpoint, and verified the signature ourselves:
# GET /root-keys -> name+keyhash+base64(0x02 || SPKI)
name, kid, mat = rootkeys.split('+', 2)
spki = b64decode(mat)[1:]
assert sha256(spki).hexdigest()[:8] == kid # c9e2f584
# GET /checkpoint -> origin \n size \n root \n\n signature lines
body = cp.split('\n\n')[0] + '\n'
sig = b64decode(line.split()[-1]) # 4-byte key hash + DER sig
pub.verify(sig[4:], body.encode(), ECDSA(SHA256())) # OK
The checkpoint we verified was signed at 09:02:17 UTC on 23 September 2026, for a tree of 351,912 entries. We then took one agent's badge (leaf 234) and walked its RFC 9162 inclusion path of 19 hashes. It recomputed the signed root exactly. We decoded the status token too. It is a COSE_Sign1 (CBOR tag 18) with an ES256 signature, kid c9e2f584 and a lifetime of exactly one hour. It verified under the same key. The receipt is also COSE_Sign1 and declares the RFC 9162 SHA-256 tree algorithm.
In short: with a 91-byte root key and a hash function, you can check that a given registration is in the log and that the log is the one the operator signed. That is a real property, and it is rarer than it should be in the agent identity space.
x001/000. The production log answers that path with HTTP 422, "index in path must be of type int64", and accepts /tile/entries/1000 instead. A generic C2SP client reads the first 256,000 entries and then stops. We walked the rest with integer paths.
The census: 351,912 events
We fetched all 1,375 entry tiles and parsed every event. All use schema V1. The first is dated 18 January 2026.
Event mix. 220,057 AGENT_REGISTERED, 131,780 AGENT_RENEWED, 75 AGENT_REVOKED. Almost all renewals (131,768) happened in September, mostly in three days: 15, 17 and 18 September. The registrations cover 219,987 distinct hostnames.
Concentration. 210,834 registrations (95.8%) are subdomains of a single domain, helpagent.club. Another 8,687 (3.9%) sit under agenthost.club. Together that is 99.8% of the log. The remaining 536 registrations spread over 56 other parent domains. The helpagent.club pattern is uniform: a support-{uuid}.helpagent.club host with a display name ending in "Customer Support Agent". The first one appears on 16 March 2026. RDAP shows both domains registered through GoDaddy, on GoDaddy nameservers, with delegationSigned: false. The registrant is redacted.
Versioning is unused. 351,711 of 351,912 events (99.94%) are for v1.0.0. The version-bound lifecycle, the feature that is supposed to catch the "quietly updated model", has almost no data to work with. That does not mean nobody updates their agents. It means updates are not being registered.
Identity is DV all the way down. Every one of the 351,912 events records a X509-DV-SERVER server certificate. No event carries a lei, a providerId or a dnssecStatus field, although all three appear in the draft's example payload. The organization-level identity the draft describes as a way to "close the gap between domain ownership and organizational identity" is absent from production data.
A third have no Identity Certificate. 74,563 registrations (33.9%), all under helpagent.club between June and August, carry only a server certificate. Without an Identity Certificate an agent cannot authenticate as a caller under either ANS-6 method. All 74,563 have server certificates expiring in September 2026, and none has been renewed. The ones we queried show badge status WARNING, which ANS-6 defines as "certificate expires soon". It is not an abuse flag.
DANE is almost absent. Only 395 registrations (0.18%) provisioned a _443._tcp TLSA record. Silver requires DNSSEC, and the two parent zones holding 99.8% of registrations are unsigned. So nearly every agent in the log is capped at Bronze plus the log check, whatever the client is willing to verify.
Revocation has never touched the big platform. Of the 75 revocations, 49 are on webmesh.ai and none are on helpagent.club. The first one we inspected carries reason code SUPERSEDED.
For a cross-check, in July ZeroFox reported about 174,000 ANS agents, 163,500 of them on helpagent.club. Counting from the log, we get 175,531 registrations sealed through 20 July, 166,447 on that domain. The numbers agree.
What a sealed display name is worth
ZeroFox also flagged an agent whose display name impersonates Zelle. It is in the log at entry 90,043, sealed on 27 May 2026, with the display name Zelle: [email protected] Customer Support Agent. It was renewed on 18 September, and its badge returned ACTIVE when we queried it on 23 September.
It is not alone in naming a payment brand. Among the registrations are display names such as support-mail-coinbase.com Customer Support Agent, do-not-replysessbinance.com Customer Support Agent, amazonrefundsupport.com Customer Support Agent and Venmo Customer Service Customer Support Agent. We cannot judge the intent behind any single one. What we can say is structural: in ANS, the display name is registrant-supplied text. The protocol seals it, which proves it was declared. It does not validate it against anything.
This is the draft's own motivating scenario turned around. A payment agent asking "does this invoicing agent belong to the supplier?" gets a cryptographically solid answer: the agent controls support-{uuid}.helpagent.club. That is true, verifiable and useless for the question asked. Domain-anchored identity is only as strong as the binding between the domain and the organization. With DV certificates and no LEI, that binding does not exist in the data.
What the protocol gets right
None of this is a case against the design. Several parts are ahead of everything else in the space:
The three-proof model in ANS-6 is the correct way to authenticate machine peers. Most agent identity schemes we have reviewed check identity and forget liveness, or check a token and forget possession.
The DPoP method accepts that real deployments terminate TLS at a CDN or gateway, and still binds each request to method, URL and body.
The transparency log is real, public and verifiable offline. Any third party can re-run this census, and that is exactly how the gaps above became visible. A registry without a log would have let us see none of it.
And the separation of identity from reputation is honest. The RA does not claim that a registered agent is safe. The problem is that a badge that says "verified" will be read as "trustworthy" by users and, increasingly, by agents.
Two protections are still missing in production. The draft names HCS-27 checkpoint anchoring as "the primary mitigation" against a compromised or colluding RA and TL operator. ANS-4 says the reference implementation "does not yet ship the HCS publishing adapter." And the IANA registrations for the ans URI scheme and the _ans labels are still future work.
What it means for LLM4Agents
We sit on both sides of an agent-to-agent call. Agents call our OpenAI-compatible gateway and pay per request in stablecoins over x402. And our endpoints are themselves something buyer agents need to identify before they sign an EIP-3009 authorization. ANS touches both.
On the inbound side, ANS registration tells us nothing about whether to extend credit or relax limits. Our trust model is already payment-first: an agent that presents a valid EIP-3009 authorization for the exact amount is good for that request, whoever it is. That design is the right one given this census. A third of registrations cannot prove possession as a caller at all. The rest prove control of a DV subdomain, mostly on one platform. What ANS-6 does add is a clean, cheap way to attach a stable, revocable identity to a caller across wallets. That is useful for rate limiting, abuse attribution and per-agent analytics, not for authorization. It is the same conclusion we reached for Web Bot Auth and the ERC-8004 registries: identity signals are inputs to policy, not policy.
On the outbound side, ANS fills a gap x402 leaves open. A 402 response tells a buyer agent to pay a payTo address. The only thing authenticating that address is the TLS session to whatever host answered. As we noted in the x402 DNS discovery audit, nothing binds a payment address to an organization. ANS does not bind wallets either. But the status token carries metadataHashes, a TL-signed hash of the agent card. If the card lists our receiving addresses, a buyer can check that the payTo in a 402 response matches a card the log has sealed. That turns "trust the TLS session" into "trust a sealed, auditable declaration."
The threat side is display names. Agent registries feed discovery. Discovery results end up in LLM context windows, where a model picks which agent to call or pay. A sealed display name like Zelle: ... Customer Support Agent is registrant-controlled text reaching a model with a trust badge attached. That is prompt-injection surface, and it belongs in our threat model alongside tool descriptions.
Staying on the frontier
Six steps, in order of value per unit of work.
1. Never map "ANS-registered" to a trust level. Registration must not change spend caps, credit or rate limits. We will write this into the gateway policy explicitly, because the default product instinct is to reward badges.
2. Accept ANS-6 Method B at the edge, logging only. Our traffic crosses a TLS-terminating edge, so mTLS (Method A) is not an option and DPoP is. Verification needs the TL root key fetched once, a replay cache for jti, and local COSE checks for the receipt and status token. It needs no per-request TL call. We will record the verified ANSName next to the paying wallet and use it for attribution before using it for anything else.
3. Register our own endpoints properly and aim for Gold. We will sign our zone with DNSSEC, publish the TLSA record and the DNS-AID SVCB records, and register with a versioned ANSName. We will register a new version on every model-routing or pricing change, which is what versioning is for. Being one of the 0.18% with DANE costs little and makes our endpoint verifiable end to end.
4. Publish payTo inside the sealed agent card. We will list our per-chain receiving addresses in the card whose hash goes into the log. Then we will propose a check to the x402 client SDKs: before signing, compare the 402 payTo with the sealed card when the payee has an ANS registration. This is the smallest change that binds a payment address to an audited declaration.
5. Treat registry text as untrusted input. Display names, descriptions and function names from any registry (ANS, the MCP registry, AGNTCY) should be quoted and sanitized before they reach a model. They should never be concatenated into instructions. The same rule we apply to tool descriptions.
6. Run our own monitor on the log. The full log is about 1,375 tile fetches, and anyone can read it. A weekly job that flags new registrations with names close to "llm4agents" or to our customers' brands costs almost nothing. It is the agent version of certificate-transparency monitoring, and the log was built to make it possible. When the operator publishes an HCS-27 topic, we will add anchored-checkpoint verification to the same job.
ANS gets the hard part right: a public, verifiable record of who declared what, and when. What it cannot do is make a DV certificate on a shared subdomain mean more than it does. For agents that move money, identity will come from payment history and organizational binding, not from the badge. The log makes that gap visible, and that is where its value is today.
Payment-first trust for agent traffic
Every request is authorized by a signed stablecoin payment, not by a badge. An OpenAI-compatible gateway, per-call settlement, no accounts to trust.
Register an agent