Two programs meet for the first time. How does either know the other is not an impostor?
This guide starts from that one question and takes ANP apart piece by piece. No cryptography or networking background required.
TIP
This is the static version; its 47 collapsible cards open and close.
For the full interactive version — sliders, toggles, live computation — open it here
Contents
Groundwork
Where identity comes from
Proving it is really you
- §06 · The five steps of signing
- §07 · That one header line
- §08 · The server's five gates
- §09 · Three attackers
- §10 · e1: key bound to identity
After the introduction
- §11 · The ad.json introduction
- §12 · Finding other programs
- §13 · When formats do not match
- §14 · Keeping content private
The big picture
§00 · First, the vocabulary
Every technical word that shows up later is explained here in plain language — come back any time. Each entry only uses ideas defined above it, so you never get a new term explained by another new term.
Identity
Agent agent
A program that can send requests on its own and answer requests on its own. Think of it as software that runs errands for you.
DID identifier
A string that stands for an identity. It works like an ID number, except it carries its own lookup address.
did:wba the flavour ANP uses
The second half is just a domain name. Whoever owns the domain owns the identity — no application to any authority.
DID document did.json
A small file hosted under that domain, containing the identity's public key. Like the front of an ID card: anyone may look.
verification method verificationMethod
One public-key entry inside a DID document. A document can hold several, each for a different purpose.
fingerprint fingerprint
A short string squeezed out of a public key. Change one character of the key and the fingerprint looks completely different.
e1 the e1 form
Writing the fingerprint into the tail of the identity string. Like stamping the key's serial number onto the ID number itself.
Security
public / private key key pair
Two matching strings of numbers. Keep the private one; publish the public one freely. Only the matching public key can check what the private key did.
signature signature
A short string computed from some content using a private key. Checking it with the public key proves two things: this private key signed it, and not one character of the content was altered.
hash / SHA-256 hash
Squeezes content of any length into a fixed-length string. Change the content a little and the result is unrecognisable.
JCS canonicalisation
Rules for writing JSON exactly one way: keys in alphabetical order, no stray whitespace. That is the only way the signer and the verifier end up with the same bytes.
nonce one-time random string
Void after a single use. Stops anyone from replaying a request you already sent.
timestamp timestamp
The moment the request was sent. The server only accepts recent ones.
end-to-end encryption E2EE
Only the two ends can read the content — not even the relay server in the middle.
Communication
HTTP header header
The few lines of explanation at the front of a network request, before the body. A postscript that comes first.
Authorization header
The one header line reserved for identity information. ANP packs the identity, the random string, the time and the signature into this single line.
access token temporary pass
A credential the server issues after verifying you once. Later requests just carry it, with no re-verification.
ad.json agent description
A program's self-introduction file: who I am, what I can do, where my interfaces are. Like the menu board outside a restaurant.
meta-protocol meta-protocol
The round where two programs, before getting down to business, agree in plain language on what format to speak in.
§01 · What the problem looks like
Say there's an assistant program on your phone. You tell it "order me a coffee." It has to go talk to the coffee shop's program. These two programs have never dealt with each other before, and they share no account system.
Each side has its own worry — expand the two panels below.
What the assistant worries about

Browsers solved this one long ago: the little padlock in the address bar (HTTPS) does exactly this. Certificates are issued by recognised authorities and cannot be faked. ANP simply reuses it rather than reinventing it.
What the coffee shop worries about

This is the one ANP is for. The hard part is that the two sides have no prior relationship — no shared account system, no password exchanged in advance.
The first worry was solved long ago by browsers — that's exactly what the little padlock (HTTPS) in the address bar is for. ANP is about the second one: how the server confirms that whoever sent the request is genuine.
§02 · The old way: every service hands you a key
Today you register an account with each service you want to use and get a password string (the industry calls it an API key). Here is what happens as the number of services grows.
Three services:
Six services:
| 3 services | 6 services | |
|---|---|---|
| Secrets you must keep | 3 | 6 |
| Sign-ups needed first | 3 | 6 |
| Parties who also know your secret | 3 | 6 |
Three problems: the count grows with every party you connect to; you must register before you can say a single word; and worst of all, both sides know the secret — so when something leaks, nobody can say who leaked it.
NOTE
In one line A shared secret means "we both know the same thing." ANP flips it: I know something only I know, and you can verify that I know it.
§03 · The new idea: the identity is itself an address you can open
An ANP identity looks like this. It reads like gibberish, but it splits into segments and each one has a job. The table below takes all five apart.

did:wba:example.com%3A8800:user:alice:e1_CfnI4TulguySDoAXRS1fm8zhAIDTxP9IByHouGTGfUY| Segment | What it is | Detail |
|---|---|---|
did | fixed prefix | Every identifier of this kind starts with did, meaning “this is a decentralised identifier.” Like the country code in front of every phone number. |
:wba | which rulebook | wba is the rulebook ANP picked. It defines how the following segments are read and where to look them up. A different method name means completely different rules. |
:example.com%3A8800 | domain (and port) | This segment decides who owns the identity: whoever controls this domain controls this identity. %3A is an escaped colon, because the colon is already taken as the separator here, so a port number has to be written this way. |
:user:alice | path | One domain can host many identities, told apart by path. user / alice later becomes two directory levels in the URL. |
:e1_CfnI4TulguySDoAXRS1fm8zhAIDTxP9IByHouGTGfUY | public-key fingerprint | The last segment is a hardening measure: e1_ followed by the fingerprint of this identity's public key. That locks the key to the identity string itself, so nobody can swap it. See §10. |
The point is that no central authority is involved anywhere: if you own a domain, you can issue your own identity, without applying to anybody.
§04 · How the other side knows where to look you up
When the server receives that identity string, it first turns it into an address it can open, then fetches the public key from there. There are four rules; below they run one at a time, with the resulting URL at the bottom.

| Rule | What it does | Why |
|---|---|---|
| 1 | Split on colons | segment 3 is the domain; every segment after it is one path level |
| 2 | Turn %3A back into a colon | the port's colon was escaped inside the identity; restore it or it collides with the separator |
| 3 | Prepend https:// | identities of this kind only travel over HTTPS — that padlock is where the trust comes from |
| 4 | Append the path, then did.json | with no path it uses /.well-known/did.json instead |
The resulting URL:
https://example.com:8800/user/alice/e1_CfnI4TulguySDoAXRS1fm8zhAIDTxP9IByHouGTGfUY/did.jsonThese four rules are fixed and every implementation follows them — so the URL you work out anywhere else will be identical to this one.
§05 · Open that address — what is inside
What comes back is the file below. This is a real one generated with the SDK, with a few minor fields removed. The table below explains each field.
{
"id": "did:wba:example.com%3A8800:user:…GTGfUY",
"verificationMethod": [
{ "id": "…#key-1", "type": "Multikey",
"publicKeyMultibase": "z6Mkt7CwZUzDo3dF4VKvmmXbmbZdvyvUirDfZ1jQVKemkXv7" },
{ "id": "…#key-2", "type": "EcdsaSecp256r1VerificationKey2019", … },
{ "id": "…#key-3", "type": "X25519KeyAgreementKey2019", … }
],
"authentication": [ "…#key-1" ],
"keyAgreement": [ "…#key-3" ],
"service": [
{ "type": "AgentDescription",
"serviceEndpoint": "https://example.com/agents/alice/ad.json" }
],
"proof": {
"type": "DataIntegrityProof", "cryptosuite": "eddsa-jcs-2022",
"proofValue": "KbDvKOJ4oqVxHhlNkcxMimQumPfIPgsgG0P9s-aKc0Lm…"
}
}| Field | What it is | Detail |
|---|---|---|
id | the identity's own number | Must match the string in your request exactly. The first thing the server does after fetching the file is compare this; a mismatch means it fetched the wrong file. |
verificationMethod | the key list | The heart of the file. Each entry is one public key with its own purpose. There are three here, so this identity has three keys. |
publicKeyMultibase | the key itself | The actual public-key value in a compact text encoding. This is what gets used to check a signature. |
authentication | which key proves identity | Picks one key from the list above and designates it for “proving I am me.” Here it points at #key-1. |
keyAgreement | which key is for encryption | Picks another one, used to negotiate encryption keys — the one that locks content. Here it points at #key-3. |
service | where else to find me | Optional. It records the URL of the self-introduction file (the ad.json of §11), so others can follow it. |
proof | the file's own signature | The whole document is signed by its own private key. Change a single character and this signature stops matching. |
Notice there is only a public key in here, never a private one. This file is public — anyone can download it — but downloading it does not let anyone impersonate you, because signing needs the private key, and the private key never leaves your device.
§06 · Proving “I am who I say I am”: five steps
With the public key posted online, the next job is to prove on the spot that you hold the matching private key. That takes five steps, like an assembly line.
Five cards below — click a title to expand it. Each one contains real output.
1. Pick four items random string, time, the other side's domain, who I am

Why What gets signed is not the whole request, only these four. One more or one fewer and the two sides compute different results.
random string 7f3a91c25d0e46b8a1c47e29f0b3d85c
time 2026-07-27T04:00:00Z
for whom example.com ← the other side's domain
who I am did:wba:example.com%3A8800:user:alice:e1…Note “For whom” is the critical one: it pins this proof to that one server, so a different server will not accept it.
2. Arrange in one canonical form strict ordering, so both sides get the same bytes

Why The same content can be written countless ways (different order, different spacing). Arranging it by one strict rule first is the only way the signer and the verifier end up with the same bytes.
{"aud":"example.com","did":"did:wba:example.com%3A8800:user:alice:e1_CfnI4TulguySDoAXRS1fm8zhAIDTxP9IByHouGTGfUY","nonce":"7f3a91c25d0e46b8a1c47e29f0b3d85c","timestamp":"2026-07-27T04:00:00Z"}Note Notice the four items were re-sorted alphabetically: aud → did → nonce → timestamp. As long as both sides follow the same strict rule, programs written in any language can verify each other's signatures.
3. Squeeze to a fixed length any-length content becomes one fixed short string

Why That line has no fixed length. Squeezing it into a short fixed string first makes the signing algorithm faster and more predictable.
2d403a0c8b772eeb176787b96f7f885488899ca4d1d33421437bd48abe8398afNote Change one character of the content and these 64 digits become unrecognisable — which is exactly where a signature's power to prove “nothing was altered” comes from.
4. Sign it with the private key the only step in the whole process that uses the private key

Why This is the one place the private key is used. The result is visible to everyone, and forgeable by nobody.
-b_gSKP85w3YOIr4cL9twlNTZF1XQbSJCdMBA4l8jyW45N-OEYzjZTRVcBPFBYUIeRX5f-EBOnqFfgQMXq-jDANote The private key was never sent anywhere. That is the fundamental difference from a shared password.
5. Pack it into the request header the finished product is that first line of the request

Why The identity, the random string, the time, which key was used and the signature all go into one line at the very front of the request.
Authorization: DIDWba v="1.1", did="did:wba:example.com%3A8800:user:alice:e1_CfnI4TulguySDoAXRS1fm8zhAIDTxP9IByHouGTGfUY", nonce="7f3a91c25d0e46b8a1c47e29f0b3d85c", timestamp="2026-07-27T04:00:00Z", verification_method="key-1", signature="-b_gSKP85w3YOIr4cL9twlNTZF1XQbSJCdMBA4l8jyW45N-OEYzjZTRVcBPFBYUIeRX5f-EBOnqFfgQMXq-jDA"Note This is what lets the server finish verification on the very first request it receives, with no preliminary “send me a challenge and I'll sign it” round.
§07 · The product: one scary-looking line of text
What those five steps produce is this single line, stuck at the very front of the request. The table below takes each piece apart: who wrote it, what it is, what it blocks, and what happens without it.
Authorization: DIDWba v="1.1",
did="did:wba:example.com%3A8800:user:alice:e1_CfnI4TulguySDoAXRS1fm8zhAIDTxP9IByHouGTGfUY",
nonce="7f3a91c25d0e46b8a1c47e29f0b3d85c",
timestamp="2026-07-27T04:00:00Z",
verification_method="key-1",
signature="-b_gSKP85w3YOIr4cL9twlNTZF1XQbSJCdMBA4l8jyW45N-OEYzjZTRVcBPFBYUIeRX5f-EBOnqFfgQMXq-jDA"| Piece | Who wrote it | What it is | What it blocks | Without it |
|---|---|---|---|---|
DIDWba | written by the assistant | The scheme name. It tells the server: read what follows by ANP's rules. | Stops it being confused with other authentication schemes. Checking these six characters is the first thing the server's parser does. | The server would not know which rules to read the rest by, and would simply error out. |
v="1.1" | written by the assistant | The protocol version. It decides what the “for whom” slot from the previous section is called. | In principle it stops old and new versions signing different content. | Once the two sides disagree about the version, the content signed and the content verified no longer match, and verification is bound to fail. |
did=… | written by the assistant | Who I am. The server uses it to work out where to fetch the public key (§04). | It is part of the signed content too, so altering it destroys the signature. | The server would have no idea which domain to fetch the key from. |
nonce=… | rolled fresh by the assistant | A one-time random string. The server remembers the ones it has seen, so the same one coming back is a replay. | Stops anyone copying the whole line and sending it again. | The line becomes a pass usable an unlimited number of times — whoever copies it can keep impersonating you. |
timestamp=… | written by the assistant | The moment it was sent. The server only accepts recent ones. | Stops stockpiling: signing a pile of lines and using them months later. It also determines how often the record of random strings can be cleaned out. | The record of random strings would have to be kept forever, or old requests become replayable again after a cleanup. |
verification_method=… | written by the assistant | Which of the three keys was used. | Stops the server picking the wrong public key — this identity has three keys with different purposes. | The server could only try each one in turn, or hard-code “always the first”, which rules out ever rotating keys. |
signature=… | computed by the assistant with the private key | The signature over the four items above. | Stops forgery and tampering: it cannot be produced without the private key, and altering any signed field invalidates it. | Every field above becomes plain text anyone can fill in, and the whole scheme amounts to nothing. |
§08 · What the server does: five gates in a row
The server does not verify the signature first — verifying means fetching the public key over the network, which is expensive. So it runs a few cheap checks first. The five cards below each tamper with this request in a different way; click a title to expand and see which gate catches it.
All five passed:
| Gate | Checks | How |
|---|---|---|
| 1 | Is the timestamp fresh | Look at the time in the header; accept only the last few minutes and drop anything older. |
| 2 | Has this random string been used | Check the recently seen random strings. A repeat is a replay. |
| 3 | Does this identity have permission | Is it a customer of this shop, may it touch this resource? Recognising you is not the same as letting you in. |
| 4 | Fetch the public key | Turn the identity into a URL by §04's rules and fetch did.json. This step goes over the network and is the slowest. |
| 5 | Verify the signature | Redo the four steps of §06 and check the signature against the public key. The most expensive step, so it goes last. |
Tamper with the request and see which gate catches it:
Set the time to an hour ago → stopped at gate 1

Gate 1 stopped it: Is the timestamp fresh. The gates after it never ran — in particular the expensive key fetch and signature check, saving a network round trip entirely.
Reuse a random string from before → stopped at gate 2

Gate 2 stopped it: Has this random string been used. The gates after it never ran — in particular the expensive key fetch and signature check, saving a network round trip entirely.
Switch to an unauthorised identity → stopped at gate 3

Gate 3 stopped it: Does this identity have permission. The gates after it never ran — in particular the expensive key fetch and signature check, saving a network round trip entirely.
Delete did.json from the domain → stopped at gate 4

Gate 4 stopped it: Fetch the public key. The gates after it never ran — in particular the expensive key fetch and signature check, saving a network round trip entirely.
Quietly alter one character of the signature → stopped at gate 5

Gate 5 stopped it: Verify the signature. The gates after it never ran — in particular the expensive key fetch and signature check, saving a network round trip entirely.
The order is deliberate: the early gates are cheap local checks that throw out the vast majority of junk, and the expensive signature check comes last.
§09 · How three attackers each come up short
The easiest way to see what those fields are for is to turn it around: what could an attacker do without them. Three attackers, one card each.
1. Copy the whole line and send it again

The plan The attacker intercepts the line your assistant sent to the shop and sends it again unchanged, hoping to place another order on your tab.
What stops it the one-time random string plus the timestamp
How The shop remembers the random strings it has seen in the last few minutes. It has seen this one, so it drops the request. And by the time that record is cleared, the timestamp is long expired.
2. Relay your proof to somebody else

The plan Your assistant is lured to a fake coffee shop run by the attacker and authenticates there as usual — so that line lands in his hands. He has no private key of yours and can sign nothing himself, so all he can do is relay the line unchanged to the real shop and order in your name.
What stops it the “which domain is this proof for” item
How That item is not in the line at all. Your assistant signs using the domain it is actually dialling (the attacker's); the real shop verifies using its own domain. The two sides compute different bytes and the signature does not match. The attacker cannot see it, cannot change it and cannot supply it.
3. Swap the public key on your domain

The plan The attacker gains control of your domain, replaces the public key inside did.json with his own, and then impersonates you using his own private key.
What stops it — neither of the first two defences stops this one
How Not stopped. The shop simply fetches the file from the URL and believes whatever it gets — this is the biggest soft spot of identity schemes of this kind. The e1 layer covered in §10 is what addresses this.
§10 · Binding the key to the identity: e1
Everything so far rests on one assumption: only you can change the file under that domain. But domains change hands — a registration lapses, a company is sold, a server is migrated — and that file ends up with whoever holds the domain next.
The SDK adds a layer called e1 by default: it binds the public key to the identity string, so the identity cannot be mistaken for someone else's when that happens. It does one thing at each of two moments. Here is the first.
1 · At issue time: the identity is computed from the public key, once and only once

So this identity is not an arbitrary name — it is a product of the public key. Swap the key and the fingerprint changes, which means the whole identity string is no longer the same string.
Now the crucial part: you handed that string out. It rides along in every request you send, and the other side wrote it down. When the domain later changes hands, the new holder can change the file on the domain, but cannot change the string that already went out and is sitting in other people's records.
2 · At every verification: one extra comparison, between gate 4 and gate 5 of §08
With e1 · domain changed hands → caught on the spot

The two fingerprints do not match, so this gate does not pass and gate 5 never runs. The file on the domain can be changed, but the identity string you handed out long ago — already recorded by others — cannot; and the tail of that string carries the fingerprint of the original key.
Without e1 · domain changed hands → indistinguishable

Verification passes as usual. The verifier cannot tell the key was replaced — the identity string holds nothing to compare against. This is what the third attacker in §09 relies on.
With e1 · domain still yours → normal pass

The two fingerprints match, so this gate passes and gate 5's signature check follows.
Put the two moments together: the fingerprint is carved in at issue time; the comparison happens on every request. The file on the domain can change; the identity string cannot — so comparing the two tells you whether they still belong together.
Which raises a fair question: can the domain's new holder still change that file? Yes. e1 does not prevent the file from being changed, but once it is, the verifying side can always tell.
3 · When a domain changes hands: what follows the domain and what does not
| Follows the domain | |
|---|---|
| The public key inside did.json | the new holder can replace it |
| Whether the file exists at all | it can also be removed entirely |
| Which document is served there | it can be swapped for another identity's |
| Does not follow the domain | |
|---|---|
| The identity string you already handed out | it lives in other people's records, not on the domain |
| The link between fingerprint and key | a different key means a different fingerprint |
| Your private key | it has never left your device |
So the verifier sees a different result:
NOTE
What e1 guarantees There are only two possible verification results: “confirmed as you” or “cannot confirm”. There is never “confirmed as someone else”.
The cost is that this identity's availability is tied to the domain: lose the domain and you have to issue a fresh identity under a new one.
This check runs automatically, and a little earlier than the diagram suggests: the moment the file comes back, two things are checked on the spot — whether the identity written in the document matches the one you asked for, and whether the fingerprint binding holds. Either failure means rejected outright, never reaching the signature check.
§11 · Identity confirmed — now: what can you do?
Knowing who the other side is is not enough; you also need to know what it can do and how to call it. ANP has every program publish a self-introduction file, usually at /ad.json — think of it as the menu board outside a restaurant.

That menu typically lists: its name, who owns it, what it can do, which URL each capability lives at, and whether authentication is required first. Another program reads it once and knows how to deal with it.
§12 · How do you know which programs are out there
The spec defines a directory: knowing only a domain, you visit one fixed path under it and get back every publicly listed program on that domain.

By this design, finding a program is the same as finding a website: the domain is enough. No prior registration on some platform, no introduction from a middleman.
§13 · When the two sides do not share a format: negotiate first
The traditional answer is manual integration: you send me an interface doc, I write code against it, and we go back and forth for weeks. ANP's idea is to let the two programs work it out in plain language, then each generate its own handling code.
The negotiation runs in four steps, one card each.
1. A opens in plain language the initiator

“I want hotel prices. I can take JSON, and I'd like fields for room type, date and price. What do you support?” — note that this round is natural language, because at this moment there is no shared format to use yet.
2. B answers in plain language the responder

“Fine. My dates are ISO format, prices come in two columns for with and without tax, and you also need to include the branch code.” The two may go back and forth several rounds until the format is settled.
3. Each side generates its own code done separately

Once settled, each side has a language model generate handling code from the agreement. This step needs no participation from the other side, and no human writing interface documents.
4. Test each other, then get to work both sides

Optionally they exchange a few test messages to confirm they understood the same thing. After that they communicate efficiently in the agreed format and drop the natural language.
This layer is still at a very early stage and plenty of details are unsettled. It is here so you know that in ANP's design, even "what format shall we speak in" is something that can be negotiated on the spot.
§14 · Last piece: can the relay server see the content
Everything so far has been about "who are you." One question remains: messages pass through a relay server — can that server read them. Expand the two panels below to see what the server sees in each case.
End-to-end encryption on → the server sees only ciphertext

End-to-end encryption off → the server sees the text

§15 · Step back: the whole thing in four blocks
The fourteen sections above were really moving between four different layers. Expand each one to see what it covers and what it deliberately leaves alone.
Identity and encrypted transport answers “who are you” and “can the content be read”
| Covers | identity format, key files, signing and verification, end-to-end encryption |
| Leaves alone | does not tell you what a program can do |
Meta-protocol layer answers “our formats do not match”
| Covers | negotiating a format in natural language, then each side generating code |
| Leaves alone | does not carry the actual data |
Application layer · introduction answers “what can you do”
| Covers | ad.json, the capability list, the call addresses |
| Leaves alone | does not verify identity; that is layer one's job |
Application layer · discovery answers “which programs are out there”
| Covers | listing every public program under one domain |
| Leaves alone | does not search across domains |