The Agent Network Protocol (ANP) is an open source protocol suite, maintained by the ANP Community under the Apache 2.0 licence, that lets AI agents on different platforms identify, find and call each other without a central registry. Identity comes from W3C Decentralized Identifiers hosted on ordinary web servers (did:wba and native did:web), requests are signed with standard HTTP Message Signatures, and agents publish machine readable descriptions at well known URLs.
Its best known idea, a meta-protocol that lets two agents agree on a protocol at run time, is still a draft. The current release, ANP 1.2, ships identity, description, discovery and messaging, and keeps negotiation optional and unreleased. This post goes one level deeper than our posts on A2A versus MCP on the wire, A2A for security teams, AGNTCY and agent identity and AP2 mandates. Those cover how agents talk inside one ecosystem and how they pay; this one covers how agents prove who they are across the open web, and what breaks when that proof rests on a domain name.
The project site, agent-network-protocol.com, did not respond to our requests on 24 October 2026 after two attempts, so every ANP fact here comes from the project's GitHub repository, its specifications and the white paper its authors posted to arXiv.
Key takeaways
- ANP is a set of numbered specifications, not a product. ANP-01 to ANP-10 plus nine messaging profiles, with Python, Go, Rust and Dart SDKs in a separate AgentConnect repository.
- The released core is identity plus description. ANP-02 (DID authentication), ANP-03 (
did:wba), ANP-04 (naming), ANP-07 (agent descriptions) and ANP-08 (discovery) are marked "Released v1.2". - The meta-protocol is draft and was redesigned. The white paper described agents generating protocol code for each other. The ANP-06 draft deprecates that flow and limits negotiation to choosing among published interfaces.
- Identity is only as strong as the web server that hosts it. Whoever controls the domain, DNS or web root controls a
did:webidentity. Thedid:wbae1_profile narrows that by putting a hash of the agent's key in the DID itself. - Authentication is not authorization. A valid signature proves which DID sent a request, nothing more.
- The W3C link is a Community Group, not a standard. ANP's white paper is a draft report of the W3C AI Agent Protocol Community Group, which carries no W3C Recommendation status.
What ANP is, who maintains it and what problem it targets
The project and its people
ANP calls itself "an open-source communication protocol for intelligent agents" and aims to become the HTTP of what its authors call the Agentic Web. Copyright notices name the "ANP Community" and date from 2024. The README also warns that the project "has not issued any digital currency on any platform or blockchain", which matters when tokens or wallets appear under the ANP name.
The technical white paper, ANP-01, was posted to arXiv on 18 July 2025 with Gaowei Chang as first author and six co-authors. The repository keeps it as an informative document at version 1.2 and states that normative requirements live in each numbered specification, not in the white paper.
The SDKs live in a sibling repository, AgentConnect. The getting started guide lists a Python package (anp), a Go module, a Rust crate and a Dart package as published, with TypeScript and Java built from source, and labels that table a snapshot checked on 27 June 2026. The README also links AWiki, an open source implementation of ANP identity and messaging with a Rust command line client, a Flutter app and a server.
The W3C connection
The W3C AI Agent Protocol Community Group launched on 8 May 2025 to "develop open, interoperable protocols that enable AI agents to discover, identify, and collaborate efficiently across the Web". When we fetched its page it listed two chairs, Gaowei Chang and Song Xu, 282 participants, and three draft reports: "Agent Network Protocol White Paper" (23 May 2025) plus "use case" and "Protocol" documents (19 August 2025). It has met biweekly since June 2025.
W3C hosts Community Groups, but the group page itself says they "do not necessarily represent the views of the W3C Membership or staff". The accurate statement is that ANP builds on W3C standards such as DID Core and that its white paper is a Community Group draft report whose chair is the white paper's lead author. ANP is not a W3C standard.
The problem ANP targets that MCP and A2A do not
ANP's README names three gaps: agents on different platforms cannot authenticate and find each other, agents imitate human browsing instead of using native interfaces, and agents cannot self-organise collaboration cheaply. The difference from protocols most teams know is the trust boundary:
- MCP connects one AI application (the host) to the tools and data it uses, with JSON-RPC 2.0 messages between hosts, clients and servers. Remote servers act as OAuth 2.1 resource servers. Trust starts from the host's configuration.
- A2A, at version 1.0.0 under the Linux Foundation, connects agents that may be "opaque" to each other through Agent Cards that declare skills, bindings and security schemes such as OAuth 2.0 or OpenID Connect. Identity usually comes from an existing identity provider.
- ANP targets agents with no prior relationship and no shared identity provider. Each identity is a DID anyone can resolve over HTTPS, so any server can verify any client without an account or a federation agreement. The white paper compares this to email: each platform runs its own accounts, yet platforms interconnect.
ANP gives up the safety net of a shared identity provider in exchange for open interconnection. Most of the risks later in this post follow from that choice.
The layers: what the white paper says and what ANP 1.2 releases
The white paper describes three layers, bottom to top: an identity and secure communication layer based on W3C DIDs, a meta-protocol layer where agents negotiate how to communicate, and an application protocol layer for agent description and discovery.
The current README is more careful. It says ANP 1.2 "organizes the released protocol capabilities into two core protocol layers plus domain-specific application protocols", built on existing HTTP, TLS, DNS, CA, CDN and search infrastructure, and that ANP-06, the meta-protocol, "remains a draft and is not part of the currently released architecture".
| Layer (white paper name) | What ANP 1.2 ships | Key documents | Status in the repository |
|---|---|---|---|
| Identity and encrypted communication | DID authentication for did:wba and native did:web, naming, device bound end-to-end encrypted messaging | ANP-02, ANP-03, ANP-04, ANP-09, profiles P1 to P9 | Released v1.2; group E2EE profile P6 is a candidate |
| Meta-protocol | Optional negotiation method anp.negotiate | ANP-06 | Draft, not released |
| Application protocols | Agent Description, Agent Discovery, domain protocols on top | ANP-07, ANP-08, ANP-10 | ANP-07 and ANP-08 released v1.2; ANP-10 (an AP2 payments adaptation) is draft |
| Authorization | OAuth client identity tied to a DID, verifiable credential delegation | ANP-05 | Draft v0.7, not released |
An article describing ANP agents writing code for each other in production is describing the 2024 to 2025 vision, not the released specifications.
How DID based identity works in ANP
DIDs and did:web
A Decentralized Identifier is a URI of the form did:method:method-specific-id. W3C published DID v1.0 as a Recommendation on 19 July 2022; DID v1.1 reached Candidate Recommendation Snapshot on 5 March 2026, and ANP cites v1.0. A DID resolves to a DID document listing verification methods (public keys), the relationships they may be used for (authentication, assertionMethod, keyAgreement) and optional service endpoints. The DID method decides where the document lives and who can change it.
The did:web method, a W3C Credentials Community Group document published with "unofficial" status, turns a domain into a DID. did:web:example.com resolves to https://example.com/.well-known/did.json; did:web:example.com:user:alice resolves to https://example.com/user/alice/did.json. Updating the identity means editing that file.
Two points from its security section shape everything below. The method "does not specify any authentication or authorization mechanism for writing to, removing or creating the DID Document", so protecting the file is the web operator's job. And DNS is the main attack surface: an attacker who can spoof or intercept DNS can point resolvers at a server with a different document. The specification recommends DNS over HTTPS and DNSSEC and requires NIST SP 800-52 Rev. 2 TLS guidance.
did:wba: did:web with a key fingerprint in the name
ANP-03 defines did:wba, "extended and optimized based on did:web". The authors chose a new method name because agreeing changes with did:web's authors would take a long time, and they leave open a later merge. ANP-03 pins the did:web version dated 31 July 2024.
did:wba has two forms. Bare domain DIDs such as did:wba:example.com resolve like did:web and serve as domain level service identity. Path DIDs identify an agent or user, and for new ones the last segment must be an e1_ fingerprint: did:wba:example.com:user:alice:e1_<fingerprint> resolves to https://example.com/user/alice/e1_<fingerprint>/did.json.
The fingerprint is derived from the agent's Ed25519 binding key, which the document publishes as a Multikey:
- Convert the key to a minimal JSON Web Key with only
crv,ktyandx. - Compute the RFC 7638 thumbprint: members in lexicographic order, no whitespace, SHA-256.
- Base64url encode the 32 byte digest without padding (always 43 characters) and prefix
e1_.
Because the fingerprint is part of the identifier, a verifier can check that a fetched document really belongs to the key the DID names. ANP-03 says this reduces "the risk of the platform silently replacing the identity public key". An active e1_ document must also carry a top level proof using the W3C Data Integrity eddsa-jcs-2022 cryptosuite, signed by the binding key, and the resolver must recompute the thumbprint from the key that signed it. If the proof is missing or signed by another key, resolution fails; ANP-03 says there is "no relaxed mode". A secp256k1 variant, k1_, exists in an appendix for wallet compatibility and is non-default.
Key discovery
Key discovery is DID resolution. A signed request carries a keyid that must be a full DID URL such as did:wba:example.com:user:alice:e1_abc#key-1. The server resolves the DID over HTTPS, checks the document id matches, checks the key exists and sits in authentication, applies the method's binding checks, and only then verifies the signature.
ANP-02 adds transport rules: HTTPS is mandatory, certificates must be matched on dNSName in subjectAltName rather than Common Name, DID documents should be fetched with DNS over HTTPS, and resolvers should not follow untrusted cross-origin redirects.
Key rotation
In did:web you edit did.json and the DID stays the same. In did:wba with e1_, a new binding key means a new fingerprint and therefore a new DID. ANP-03 links them with a migration chain:
- The new DID keeps the same stable subject path, everything before the fingerprint (for example
example.com:user:alice). That path may never be reassigned to another subject. - The old document stays online with
deactivated: trueandsuccessorDidnaming the new DID, so historical signatures remain verifiable. Deleting it is not allowed. - The old document's proof covers the deactivation and successor fields.
Verifiers grade each hop. A hop signed by the old binding key is verified; one signed by a recovery key that an earlier trusted document pre-authorised is recovery_verified; an unsigned chain fetched entirely from one authenticated HTTPS origin is at best provider_asserted; anything else, including a naming record or a redirect hint, is unverified. Clients should cache only the first two. A server may answer a superseded DID with 409 Conflict and a did_superseded body, but ANP-03 says its currentDid field is only a hint: the client must walk and verify the chain.
Human readable names live in ANP-04's naming service (WNS handles), so the DID can change with the key while the handle stays stable. ANP-03 notes that updating a handle "does not replace the migration proof".
Native did:web keeps its simpler rules. ANP-02 Appendix B lets did:web identities use the same request authentication and forbids imposing did:wba proof rules on them. Convenient, but a did:web agent's valid key is whatever the hosted file says today.
How a request is signed
ANP-02 reuses IETF standards. Requests carry Signature-Input and Signature headers from RFC 9421 (HTTP Message Signatures) and, with a body, a Content-Digest header from RFC 9530. The signature must cover @method and @target-uri, plus content-digest when there is a body. Parameters must include created, should include expires, and may include a nonce, which becomes mandatory when the server issued one in a challenge.
The server checks, in order: headers present, body digest matches, keyid resolves under its method rules, the key is in authentication, the method binding holds, the rebuilt signature base verifies, created and expires fall within a window (1 to 5 minutes recommended), and the (keyid, nonce) pair is not in a short term replay cache. Only then does it decide, separately, whether this DID may do what it asked. Failures return 401 with a WWW-Authenticate: DIDWba challenge; authenticated but unauthorised callers get 403.
After a successful first request the server may return an access token, recommended as a JWT, in the Authentication-Info header, which the client then sends as a Bearer token. ANP-02 prefers sender-constrained tokens bound to the client's key but says that profile is "not yet fully defined".
Meta-protocol negotiation: what changed and why it matters for safety
The original idea
The meta-protocol draws on Agora, from the paper "A Scalable Communication Protocol for Networks of Large Language Models", submitted to arXiv on 14 October 2024, in which agents use standard routines for frequent exchanges, natural language for rare ones and LLM-written routines in between.
The white paper describes six steps: agent A sends a natural language request with candidate protocols; agent B uses AI to accept or counter-propose; once they agree, "each party generates and deploys protocol processing code"; they negotiate test data and jointly test the AI-generated code; they go live; and they repeat when requirements change. Results can be cached and shared for reuse.
The current draft
ANP-06, still a draft, recasts the meta-protocol as a "semantic negotiation control plane" that carries no business data:
- Read the target's Agent Description and find a
MetaProtocolInterfaceentry. - Call
anp.get_capabilitiesto confirm what the target supports now. - Call
anp.negotiate(JSON-RPC 2.0, profileanp.meta.negotiation.v1) with an intent, the caller's supported profiles, security profiles and content types, constraints such asrequiresHumanAuthorization, and candidate interfaces. - Receive a
NegotiationResultnaming the selected interface, protocol, security profile, schemas, an expiry (validUntil) and anegotiationDigest. - Make the business call with that selection.
Natural language "protocol drafting" survives only as a fallback, and its output "should not be treated as a stable protocol before both parties accept it". Three statements change the safety picture. The non-goals include "mandatory AI code generation, remote code loading, or code exchange". Section 12.5 says any code generation or loading "is a local implementation detail and must be handled in a sandboxed, least-privilege environment". And section 13 deprecates the early binary PT=00 model, including its codeGeneration and fixErrorNegotiation flows.
Safety implications of agents generating protocol code
The original idea still deserves a threat model, because experimental implementations may exist and the natural language fallback invites generating parsers on the fly.
When your model writes code to handle messages another agent defined, that agent influences code running in your process. It is a supply chain with one hop. A hostile counterpart can steer generated handlers through descriptions, field names, examples and test data, much as prompt injection steers tool use. Code "tested" against the counterpart's own test cases only proves it handles the counterpart's chosen inputs.
ANP-06's own controls are a sound base: no silent downgrade from a stronger security profile, negotiation results are not authorization, minimal disclosure and redacted logs, and checks on any referenced protocol artifact for digest, publisher, schema integrity, dependencies, and "unsafe code, remote execution instructions, or excessive permission requirements".
Agent descriptions and discovery
ANP-07 defines the Agent Description Protocol. An Agent Description is a JSON-LD document, conventionally an ad.json file, with protocolType fixed to ANP, a protocolVersion, name, did, owner and description, plus:
- Required
securityDefinitionsandsecurity. The example scheme isdidwba; theAuthorizationheader it names carries only the optional follow-up token, while first requests use the ANP-02 signature headers. - An
interfacesarray ofNaturalLanguageInterfaceandStructuredInterfaceentries, where structured interfaces name a protocol such asopenrpc,MCP,YAMLorWebRTC. - An optional
humanAuthorization: trueflag for sensitive interfaces. ANP-07 says it "is not a DID verification relationship, an authorization credential, or proof that authorization has been completed". - An optional
proofby the agent's DID key with adomainthat clients must compare to where they fetched the document.
Secrets that grant access "must not be stored in the AD"; the description says how to reach an agent once authorised. A DID document can point at it through a service entry of type AgentDescription.
ANP-08 defines discovery. Active discovery uses an RFC 8615 well known URI: https://{domain}/.well-known/agent-descriptions returns a JSON-LD CollectionPage listing every public agent description under the domain, paginated with next. Passive discovery lets an agent register its description URL with a search service agent through that agent's declared API. Its security guidance asks search services to validate descriptions, authenticate registrations with ANP-02, rate limit, and list only public agents.
MCP, A2A and ANP compared
| Dimension | MCP | A2A | ANP |
|---|---|---|---|
| Main relationship | One AI application to its tools and data | Agent to agent across organisations or frameworks | Agent to agent across the open web with no prior relationship |
| Topology | Host with clients, each connected to one server | Client agent calls a remote agent | Peer to peer over HTTPS; any agent can call any agent it can resolve |
| Identity model | Remote servers are OAuth 2.1 resource servers; clients register or use Client ID Metadata Documents | Security schemes in the Agent Card (OAuth 2.0, OpenID Connect and others); cards can be JWS signed | W3C DIDs (did:wba, native did:web), RFC 9421 signed requests, optional token after first contact |
| Discovery | Configured by the host or user | Agent Card, commonly at /.well-known/agent-card.json | Agent Descriptions, /.well-known/agent-descriptions, search agent registration, DID service entries |
| Wire format and transport | JSON-RPC 2.0 over stdio or Streamable HTTP | JSON-RPC, gRPC or HTTP+JSON bindings | JSON-RPC 2.0 core binding, OpenRPC interfaces, messaging profiles with device bound E2EE |
| Run time negotiation | Per-request capability negotiation | Client picks a declared binding | Optional anp.negotiate (draft) |
| Status | Dated versions; latest is 2026-07-28 | 1.0.0, Linux Foundation | ANP 1.2 on GitHub; white paper is a W3C Community Group draft report |
The three can coexist. ANP-07 lists MCP as a structured interface protocol, so an ANP agent can advertise an MCP server as one way to reach it.
Risks and controls
These risks follow from the design, not from a known incident; we found no public report of an exploited ANP deployment.
1. DID documents hosted on a compromised domain
For did:web and bare did:wba identities, anyone with write access to the web root, CDN, DNS zone or domain registration can replace the keys and sign as the agent. A spoofed DNS answer or a mis-issued certificate does the same for clients that resolve through it.
Controls: prefer did:wba e1_ path DIDs for agents, so a swapped key fails the fingerprint check. Keep did.json in version control, deploy it through a reviewed pipeline and alert on changes, as both did:web and ANP-03 suggest. Lock the domain at the registrar, enable DNSSEC, monitor Certificate Transparency for identity domains, resolve over DNS over HTTPS, refuse cross-origin redirects, and alert when a known DID's keys change outside a verified rotation chain.
2. Impersonation and look-alike identities
A look-alike domain, a misleading name or owner, or a handle pointing at the wrong DID can fool a person or an LLM-driven agent while every signature verifies. A valid DID proves control of a key and a domain, not who owns the domain.
Controls: allowlist trusted DIDs or domains for anything sensitive and give unknown agents read only interfaces at most. Pin by full DID including the e1_ fingerprint, never by display name. Verify the description's proof and its domain. Show users the resolved domain before approving a humanAuthorization action.
3. Replay and signature scope gaps
Without body coverage, expires, a short window or a replay cache, a captured request can be replayed or altered. Follow ANP-02 as written: cover content-digest whenever there is a body, keep the window between one and five minutes, keep a (keyid, nonce) cache that outlives signature lifetimes, use the operating system's secure random source for nonces, and use server issued nonces on high value endpoints.
4. Negotiated protocols that skip validation
Negotiation can select a weaker security profile, a natural language path where a schema validated one existed, or an unreviewed external artifact. Controls: reject any result weaker than the profile you required, validate every business message against the negotiated schemas fetched from the counterpart's own domain or a pinned digest, never run generated or downloaded handler code in the agent process (use a sandbox with no network, no secrets and human review), expire cached results at validUntil, and renegotiate when the counterpart's DID document changes.
5. Data leakage between agents
Agents share intents and user context with counterparts found minutes earlier, and one long lived DID lets counterparts correlate a user's activity. Controls: minimal negotiation payloads with no payment or identity documents, separate DIDs per role as ANP-02 recommends, the direct E2EE messaging profile when content crosses relays with no fallback to transport only protection, and redaction plus retention limits on logs.
6. Stolen keys and bearer tokens
A stolen private key or Bearer token allows impersonation until rotation or expiry. Keep agent keys in a hardware or cloud key store, issue short lived narrow tokens, and pre-authorise a recovery key in assertionMethod so a compromised binding key can be rotated with a recovery_verified chain. ANP-02 says IP address and User-Agent are only auxiliary signals, never the token binding. Install SDKs only from the package names and repositories the project links.
| Risk | Primary control | ANP reference |
|---|---|---|
| Hosted DID document replaced | e1_ binding, change monitoring, DNSSEC, DNS over HTTPS | ANP-03 sections 2.2 and 2.6; did:web security considerations |
| Look-alike impersonation | DID allowlists, pin full DID, verify description proof domain | ANP-07 proof section |
| Replay | Digest coverage, short window, nonce cache | ANP-02 sections 3.2 and 7 |
| Downgrade or unvalidated protocol | Refuse weaker profiles, schema validation, sandbox generated code | ANP-06 sections 12.4 and 12.5 |
| Data leakage | Minimal disclosure, multiple DIDs, E2EE profiles | ANP-06 section 12.3; ANP-02 section 6 |
| Key or token theft | Key store, short tokens, recovery key | ANP-02 section 7; ANP-03 section 2.5 |
Step by step: build a did:web identity for a lab agent and verify a signed request
This lab builds the ANP-02 identity and signature checks by hand so you can see each one. It uses native did:web, which ANP-02 Appendix B accepts without the did:wba proof requirement, and prints the e1_ segment the same key would carry under did:wba. Use lab hostnames that do not exist on the internet. In production, use a maintained SDK and a key store.
Step 1: two lab hostnames with local TLS
DID resolution needs HTTPS with certificate checks, so use a server with a local certificate authority. This example uses Caddy.
echo "127.0.0.1 agent-a.lab.test agent-b.lab.test" | sudo tee -a /etc/hosts
mkdir -p ~/anp-lab/www/agents/demo ~/anp-lab/keys ~/anp-lab/logs && cd ~/anp-lab
python3 -m venv .venv && . .venv/bin/activate && pip install cryptography
cat > Caddyfile <<'EOF'
agent-a.lab.test {
tls internal
root * /home/YOU/anp-lab/www
file_server
}
agent-b.lab.test {
tls internal
reverse_proxy 127.0.0.1:8081
}
EOF
caddy run --config Caddyfile
Replace /home/YOU, then point Python at Caddy's local root certificate so verification stays on (Linux default shown; on macOS it is under ~/Library/Application Support/Caddy):
export SSL_CERT_FILE="$HOME/.local/share/caddy/pki/authorities/local/root.crt"
Never disable certificate verification to make this work. ANP-02 requires the client to check the counterpart's certificate strictly, and a resolver without verification is exactly the impersonation risk above.
Step 2: generate the key and DID document
keygen.py creates an Ed25519 key, encodes it as a Multikey (multicodec prefix 0xed01 plus the raw key, base58btc with a z prefix), writes the did:web document and prints the e1_ segment.
# keygen.py
import json, base64, hashlib, os, pathlib
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def b58(data):
n, out = int.from_bytes(data, "big"), ""
while n:
n, r = divmod(n, 58)
out = B58[r] + out
return "1" * (len(data) - len(data.lstrip(b"\0"))) + out
def b64u(b):
return base64.urlsafe_b64encode(b).rstrip(b"=").decode()
sk = Ed25519PrivateKey.generate()
pk = sk.public_key().public_bytes(serialization.Encoding.Raw,
serialization.PublicFormat.Raw)
did = "did:web:agent-a.lab.test:agents:demo"
doc = {
"@context": ["https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1"],
"id": did,
"verificationMethod": [{"id": did + "#key-1", "type": "Multikey",
"controller": did,
"publicKeyMultibase": "z" + b58(bytes([0xED, 0x01]) + pk)}],
"authentication": [did + "#key-1"],
"assertionMethod": [did + "#key-1"],
}
pathlib.Path("www/agents/demo/did.json").write_text(json.dumps(doc, indent=2))
key = pathlib.Path("keys/agent-a.pem")
key.write_bytes(sk.private_bytes(serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8, serialization.NoEncryption()))
os.chmod(key, 0o600)
jwk = {"crv": "Ed25519", "kty": "OKP", "x": b64u(pk)} # RFC 7638 members
canon = json.dumps(jwk, sort_keys=True, separators=(",", ":")).encode()
print("did:web DID:", did)
print("did:wba e1_ segment:", "e1_" + b64u(hashlib.sha256(canon).digest()))
python keygen.py
curl -s https://agent-a.lab.test/agents/demo/did.json | python -m json.tool
To publish the did:wba form you would also add the eddsa-jcs-2022 proof ANP-03 requires. Generate it with a maintained Data Integrity library or the ANP SDK; the canonicalisation and hashing steps are easy to get subtly wrong by hand.
Step 3: sign a request
sign.py computes Content-Digest (RFC 9530), builds the RFC 9421 signature base over @method, @target-uri, @authority and content-digest, and signs it.
# sign.py
import base64, hashlib, json, secrets, time, urllib.request
from cryptography.hazmat.primitives import serialization
KEYID = "did:web:agent-a.lab.test:agents:demo#key-1"
URL, AUTH = "https://agent-b.lab.test/api/echo", "agent-b.lab.test"
sk = serialization.load_pem_private_key(open("keys/agent-a.pem", "rb").read(), None)
body = json.dumps({"hello": "agent-b"}).encode()
digest = "sha-256=:" + base64.b64encode(hashlib.sha256(body).digest()).decode() + ":"
t = int(time.time())
params = ('("@method" "@target-uri" "@authority" "content-digest")'
f';created={t};expires={t + 60}'
f';nonce="{secrets.token_urlsafe(16)}";keyid="{KEYID}"')
base = "\n".join(['"@method": POST', f'"@target-uri": {URL}',
f'"@authority": {AUTH}', f'"content-digest": {digest}',
f'"@signature-params": {params}']).encode()
sig = base64.b64encode(sk.sign(base)).decode()
req = urllib.request.Request(URL, data=body, method="POST", headers={
"Content-Type": "application/json", "Content-Digest": digest,
"Signature-Input": f"sig1={params}", "Signature": f"sig1=:{sig}:"})
with urllib.request.urlopen(req) as r:
print(r.status, r.read().decode())
Step 4: verify the request the way ANP-02 describes
verify_server.py is agent B, behind the lab proxy on port 8081. It runs the ANP-02 checks in order and keeps authorization as a separate final step.
# verify_server.py
import base64, hashlib, json, re, time, urllib.request, urllib.parse
from http.server import BaseHTTPRequestHandler, HTTPServer
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def unb58(s):
n = 0
for c in s:
n = n * 58 + B58.index(c)
return b"\0" * (len(s) - len(s.lstrip("1"))) + n.to_bytes((n.bit_length() + 7) // 8, "big")
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, *a, **k):
raise ValueError("redirect refused during DID resolution")
OPENER = urllib.request.build_opener(NoRedirect)
def did_web_url(did):
parts = did.split(":")[2:]
path = "/".join(parts[1:]) or ".well-known"
return f"https://{urllib.parse.unquote(parts[0])}/{path}/did.json"
REPLAY, WINDOW = {}, 300
ALLOWED = {"did:web:agent-a.lab.test:agents:demo"}
def verify(method, url, authority, h, body):
si, sg = h.get("Signature-Input"), h.get("Signature")
if not si or not sg:
return 401, "missing signature headers"
if body and h.get("Content-Digest") != "sha-256=:" + base64.b64encode(
hashlib.sha256(body).digest()).decode() + ":":
return 401, "invalid_content_digest"
comps, rest = re.match(r'sig1=(\(.*?\))(;.*)$', si).groups()
p = dict(re.findall(r';(\w+)="?([^";]*)"?', rest))
if body and '"content-digest"' not in comps:
return 401, "body not covered by signature"
now = int(time.time())
if abs(now - int(p["created"])) > WINDOW or now > int(p.get("expires", now)):
return 401, "signature outside time window"
keyid = p["keyid"]; did = keyid.split("#")[0]
doc = json.load(OPENER.open(did_web_url(did), timeout=5))
if doc.get("id") != did or keyid not in doc.get("authentication", []):
return 401, "key not authorised for authentication"
vm = next(v for v in doc["verificationMethod"] if v["id"] == keyid)
raw = unb58(vm["publicKeyMultibase"][1:])
assert raw[:2] == b"\xed\x01"
vals = {"@method": method, "@target-uri": url, "@authority": authority,
"content-digest": h.get("Content-Digest")}
lines = [f'"{c}": {vals[c]}' for c in re.findall(r'"([^"]+)"', comps)]
lines.append(f'"@signature-params": {comps}{rest}')
sig = base64.b64decode(re.match(r'sig1=:(.*):$', sg).group(1))
Ed25519PublicKey.from_public_bytes(raw[2:]).verify(sig, "\n".join(lines).encode())
if REPLAY.get((keyid, p.get("nonce")), 0) > now:
return 401, "invalid_nonce (replay)"
REPLAY[(keyid, p.get("nonce"))] = now + 2 * WINDOW
if did not in ALLOWED:
return 403, "authenticated but not authorised"
return 200, f"verified {did}"
class H(BaseHTTPRequestHandler):
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", 0)))
host = self.headers["Host"]
try:
code, msg = verify("POST", f"https://{host}{self.path}", host, self.headers, body)
except Exception as e:
code, msg = 401, f"verification failed: {type(e).__name__}"
self.send_response(code)
if code == 401:
self.send_header("WWW-Authenticate", 'DIDWba realm="agent-b.lab.test"')
self.end_headers()
self.wfile.write(msg.encode())
HTTPServer(("127.0.0.1", 8081), H).serve_forever()
python verify_server.py # terminal 1
python sign.py # terminal 2, expect: 200 verified did:web:agent-a.lab.test:agents:demo
Then break each check on purpose. Change one character of publicKeyMultibase in did.json and the request fails: whoever can edit that file decides which key is valid, which is the hosted document risk in miniature. Send the same signed request twice and the second fails on the nonce cache. Remove the DID from ALLOWED and the request authenticates but gets 403. A production verifier also needs full RFC 9421 structured field parsing, the did:wba binding and proof checks, cached documents with change alerts, DNS over HTTPS and a shared replay store.
Step 5: log every negotiation
If you try ANP-06, record each negotiation so you can audit what was agreed and catch downgrades. This wrapper writes one redacted JSON line per result and refuses a weaker security profile:
# negotiation_log.py
import json, hashlib, time
STRENGTH = {"transport-protected": 1, "direct-e2ee": 2}
def log_negotiation(request, result, required="transport-protected",
path="logs/negotiations.jsonl"):
body, sel = request["params"]["body"], result.get("selected", {})
entry = {
"ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"target": request["params"]["meta"]["target"]["did"],
"intent_tags": body["intent"].get("intentTags", []),
"intent_hash": hashlib.sha256(body["intent"].get("description", "").encode()).hexdigest(),
"status": result.get("status"), "interface": sel.get("interface"),
"protocol": sel.get("protocol"), "security_profile": sel.get("securityProfile"),
"valid_until": result.get("validUntil"), "digest": result.get("negotiationDigest"),
"downgrade": STRENGTH.get(sel.get("securityProfile"), 0) < STRENGTH[required],
}
with open(path, "a") as f:
f.write(json.dumps(entry) + "\n")
if entry["downgrade"]:
raise RuntimeError("negotiated security profile weaker than required")
return entry
jq -c 'select(.protocol == null or .downgrade == true)' logs/negotiations.jsonl
Storing a hash of the free text intent follows ANP-06's advice that negotiation logs "should be redacted". When you finish, stop Caddy and the verifier, remove the /etc/hosts lines and delete ~/anp-lab/keys.
Maturity: what is specified, what is draft, what is only in papers
| Item | Status we found on 24 October 2026 |
|---|---|
| W3C DID Core v1.0 | Recommendation, 19 July 2022 |
| W3C DID v1.1 | Candidate Recommendation Snapshot, 5 March 2026 |
| did:web method | Unofficial W3C Credentials Community Group draft; ANP-03 pins the 31 July 2024 version |
| ANP-02, ANP-03, ANP-04, ANP-07, ANP-08 | Released v1.2 |
| ANP-09 messaging, profiles P1 to P9 | Published v1.2 catalog; P6 group E2EE is a candidate pending a registered MLS extension type |
| ANP-05 DID based authorization | Draft v0.7, not released |
| ANP-06 meta-protocol | Draft, not part of the released architecture |
| ANP-10 payments (AP2 adaptation) | Draft, not a stable payment standard |
| AI-generated protocol code, joint testing, shared negotiated protocols | Described in the white paper (arXiv 2508.00007) and the Agora paper; deprecated as an interoperability path in ANP-06 |
| ANP white paper at W3C | Community Group draft report, 23 May 2025 |
Two cautions apply. "Version: 1.2" is a document release number, not a wire version; profiles keep their own identifiers such as anp.direct.e2ee.v2. And the README says publishing a specification "does not establish SDK or product implementation support", so check what your SDK actually implements.
Common mistakes
- Calling ANP a W3C standard. It builds on W3C DID standards; its white paper is a Community Group draft report.
- Describing the white paper's code generating meta-protocol as shipped. It is draft and was redesigned away from code generation.
- Assuming key binding for bare domain or native did:web identities. Only
did:wbae1_path DIDs tie the identifier to a key. - Disabling TLS verification or following redirects during DID resolution, then shipping that configuration.
- Treating a valid signature,
humanAuthorization: trueor a negotiation result as permission. Build the403path. - Signing POST requests without
content-digestcoverage. - Deleting an old
did:wbadocument after rotation, or trusting a409response'scurrentDidwithout walking the chain. - Putting secrets in an agent description, which is public by design.
FAQ
What is the Agent Network Protocol in simple terms?
An open source set of specifications that lets AI agents from different companies prove who they are, publish what they can do and call each other over HTTPS. Identity is a W3C Decentralized Identifier hosted on the operator's domain, and requests are signed with HTTP Message Signatures.
Is ANP a W3C standard?
No. It builds on W3C standards such as DID Core, and the W3C AI Agent Protocol Community Group lists the ANP white paper as a draft report. Community Group reports are not W3C Recommendations.
What is did:wba and how is it different from did:web?
ANP's DID method, derived from did:web and resolved the same way. New path DIDs end with an e1_ SHA-256 thumbprint of the agent's Ed25519 key, and active documents need a Data Integrity proof by that key, so a replaced key fails verification. Key rotation creates a new DID linked to the old one by a signed successorDid chain.
How is ANP different from MCP?
MCP connects one AI application to the tools and data it is configured to use, over stdio or Streamable HTTP with OAuth 2.1 for remote servers. ANP connects agents with no prior relationship through DIDs anyone can resolve. An ANP agent can list an MCP server as one of its interfaces.
How is ANP different from A2A?
Both connect agents. A2A publishes Agent Cards and relies on declared schemes such as OAuth 2.0 or OpenID Connect. ANP publishes Agent Descriptions and uses DIDs with RFC 9421 signatures, so it needs no shared identity provider.
Do ANP agents write code for each other?
Not in the released specifications. The white paper described it; the ANP-06 draft deprecates that flow, selects among published interfaces, and says any local code generation must be sandboxed with least privilege.
Where does an ANP agent publish its description?
At any HTTPS URL, conventionally an ad.json file, linked from the DID document's AgentDescription service entry. A domain can list its public agents at /.well-known/agent-descriptions, and agents can register with search service agents.
Is ANP safe to use in production?
Its identity layer uses well understood standards, but an agent's identity is only as secure as its domain, DNS and hosting, and authorization and negotiation are drafts. Use allowlists, e1_ identities, short lived tokens and separate authorization checks, and confirm what your SDK implements.
Where Safeguard fits
Safeguard's fit with ANP is indirect. Safeguard does not resolve DIDs, verify HTTP message signatures, monitor DNS or certificate logs, or inspect agent negotiation traffic. It covers the software ANP agents are built from: an SDK such as the anp Python package or the Go module, an HTTP framework, cryptography libraries and a container image. safeguard scan --dir . inventories those components and matches them against vulnerability databases, --image does the same for the container you ship, reachability analysis ranks the findings your code actually calls, and the platform shows which projects contain a given component and version when an advisory lands. Policies and gates can stop a build that brings in a known vulnerable version.
An ANP agent's identity checks are only as trustworthy as the code that performs them: the SDK that parses signatures, the library that verifies Ed25519 and the framework that serves did.json. Those are open source dependencies that need the same patch discipline as any other, which is where the steps below apply.
Fix it without waiting for upstream
Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would help
The first demo connects Claude to the Safeguard MCP server with OAuth and shows how you choose which tools Claude may use. It is the MCP side of the comparison above: a host connecting to one server, with the user deciding what that server can do. The same habit of granting only the tools an agent needs applies when you decide which ANP interfaces an unknown agent may call.
Click through it at your own pace, or open it full screen.
The second demo is a real Safeguard CLI session: install, open the interactive terminal with safeguard, ask it to fix the lodash CVE in a repository, and watch it read the finding and advisory, ask before each command, update package.json to lodash 4.17.21, run npm install and npm test with your approval, and push nothing. The same flow applies to an ANP agent's repository when an SDK or cryptography dependency needs an upgrade you approve.
Click through it at your own pace, or open it full screen.
The third demo sets up the local runner on your own machine, approves it with its device code and scans from the terminal. That suits the lab above: scan the lab directory and its virtual environment on the machine where you built it.
Click through it at your own pace, or open it full screen.
To try it on your own agent code, download Safeguard and follow the CLI documentation.
Quick check
Why does a did:wba e1_ identifier resist a swapped key better than a native did:web identifier?
The last segment of an e1_ DID is a SHA-256 thumbprint of the agent's Ed25519 key, and an active document must carry a Data Integrity proof by that key. If someone replaces the key in the hosted file, the recomputed thumbprint no longer matches and resolution fails. A native did:web identifier has no such binding.
Does a successful anp.negotiate response mean the caller may go ahead with a booking or payment?
No. ANP-06 says a negotiation result only describes how the next interaction should happen. It does not mean the user approved the action or that the server authorised it, and interfaces that need human authorization still need it through the business protocol.
Sources
- ANP GitHub repository and README (ANP 1.2 index, release status, licence)
- ANP-01: Technical White Paper (informative v1.2)
- ANP-02: DID Authentication Protocol
- ANP-03: did:wba Method Specification
- ANP-04: ANP-DID:WBA Name Space Specification
- ANP-05: DID-Based Authorization (draft)
- ANP-06: Agent Communication Meta-Protocol (draft)
- ANP-07: Agent Description Protocol
- ANP-08: Agent Discovery Protocol
- ANP-09: End-to-End Instant Messaging overview
- ANP Appendix B: Native did:web integration
- ANP Getting Started Guide
- arXiv 2508.00007: Agent Network Protocol Technical White Paper (submitted 18 July 2025)
- arXiv 2410.11905: A Scalable Communication Protocol for Networks of Large Language Models (submitted 14 October 2024)
- W3C AI Agent Protocol Community Group
- W3C: Decentralized Identifiers (DIDs) v1.0
- W3C: Decentralized Identifiers (DIDs) v1.1
- W3C Credentials Community Group: did:web Method Specification
- W3C: Verifiable Credential Data Integrity 1.0
- W3C: Data Integrity EdDSA Cryptosuites v1.0
- RFC 9421: HTTP Message Signatures
- RFC 9530: Digest Fields
- RFC 7638: JSON Web Key (JWK) Thumbprint
- RFC 8615: Well-Known Uniform Resource Identifiers
- Model Context Protocol specification, 2026-07-28
- Model Context Protocol: Transports
- Model Context Protocol: Authorization
- Model Context Protocol: Architecture overview
- A2A Protocol specification
Last reviewed: 24 October 2026. ANP-05, ANP-06 and ANP-10 are drafts and the specification set changes often; check the repository README for current status before you build.