Skip to content

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

After the introduction

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

Agentagent

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.

DIDidentifier

A string that stands for an identity. It works like an ID number, except it carries its own lookup address.

did:wbathe flavour ANP uses

The second half is just a domain name. Whoever owns the domain owns the identity — no application to any authority.

DID documentdid.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 methodverificationMethod

One public-key entry inside a DID document. A document can hold several, each for a different purpose.

fingerprintfingerprint

A short string squeezed out of a public key. Change one character of the key and the fingerprint looks completely different.

e1the 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 keykey 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.

signaturesignature

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-256hash

Squeezes content of any length into a fixed-length string. Change the content a little and the result is unrecognisable.

JCScanonicalisation

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.

nonceone-time random string

Void after a single use. Stops anyone from replaying a request you already sent.

timestamptimestamp

The moment the request was sent. The server only accepts recent ones.

end-to-end encryptionE2EE

Only the two ends can read the content — not even the relay server in the middle.

Communication

HTTP headerheader

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 tokentemporary pass

A credential the server issues after verifying you once. Later requests just carry it, with no re-verification.

ad.jsonagent 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-protocolmeta-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 aboutThe assistant worries the other end is not the real shop

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 aboutThe shop worries the visitor is an impostor

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:
Connecting to three services

Six services:
Connecting to six services

3 services6 services
Secrets you must keep36
Sign-ups needed first36
Parties who also know your secret36

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.

The identity is both an identifier and a reachable address

did:wba:example.com%3A8800:user:alice:e1_CfnI4TulguySDoAXRS1fm8zhAIDTxP9IByHouGTGfUY
SegmentWhat it isDetail
didfixed prefixEvery identifier of this kind starts with did, meaning “this is a decentralised identifier.” Like the country code in front of every phone number.
:wbawhich rulebookwba 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%3A8800domain (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:alicepathOne domain can host many identities, told apart by path. user / alice later becomes two directory levels in the URL.
:e1_CfnI4TulguySDoAXRS1fm8zhAIDTxP9IByHouGTGfUYpublic-key fingerprintThe 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.

The server turns the identity into a URL and fetches the key file

RuleWhat it doesWhy
1Split on colonssegment 3 is the domain; every segment after it is one path level
2Turn %3A back into a colonthe port's colon was escaped inside the identity; restore it or it collides with the separator
3Prepend https://identities of this kind only travel over HTTPS — that padlock is where the trust comes from
4Append the path, then did.jsonwith no path it uses /.well-known/did.json instead

The resulting URL:

https://example.com:8800/user/alice/e1_CfnI4TulguySDoAXRS1fm8zhAIDTxP9IByHouGTGfUY/did.json

These 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.

jsonc
{
  "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…"
  }
}
FieldWhat it isDetail
idthe identity's own numberMust 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.
verificationMethodthe key listThe heart of the file. Each entry is one public key with its own purpose. There are three here, so this identity has three keys.
publicKeyMultibasethe key itselfThe actual public-key value in a compact text encoding. This is what gets used to check a signature.
authenticationwhich key proves identityPicks one key from the list above and designates it for “proving I am me.” Here it points at #key-1.
keyAgreementwhich key is for encryptionPicks another one, used to negotiate encryption keys — the one that locks content. Here it points at #key-3.
servicewhere else to find meOptional. It records the URL of the self-introduction file (the ad.json of §11), so others can follow it.
proofthe file's own signatureThe 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 amPick four items

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 bytesArrange in one canonical form

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 stringSqueeze to a fixed length

Why That line has no fixed length. Squeezing it into a short fixed string first makes the signing algorithm faster and more predictable.

2d403a0c8b772eeb176787b96f7f885488899ca4d1d33421437bd48abe8398af

Note 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 keySign it with 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-jDA

Note 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 requestPack it into the request header

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.

http
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"
PieceWho wrote itWhat it isWhat it blocksWithout it
DIDWbawritten by the assistantThe 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 assistantThe 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 assistantWho 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 assistantA 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 assistantThe 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 assistantWhich 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 keyThe 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:
A request passing through five gates in turn

GateChecksHow
1Is the timestamp freshLook at the time in the header; accept only the last few minutes and drop anything older.
2Has this random string been usedCheck the recently seen random strings. A repeat is a replay.
3Does this identity have permissionIs it a customer of this shop, may it touch this resource? Recognising you is not the same as letting you in.
4Fetch the public keyTurn the identity into a URL by §04's rules and fetch did.json. This step goes over the network and is the slowest.
5Verify the signatureRedo 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 1Set the time to an hour ago

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 2Reuse a random string from before

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 3Switch to an unauthorised identity

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 4Delete did.json from the domain

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 5Quietly alter one character of the signature

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 againCopy the whole line and send it again

The planThe 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

HowThe 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 elseRelay your proof to somebody else

The planYour 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 domainSwap the public key on your domain

The planThe 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

HowNot 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

At issue time the key fingerprint is carved into the identity

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 spotWith e1 the swapped key is caught

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 → indistinguishableWithout e1 there is nothing to compare

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 passThe normal case

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.jsonthe new holder can replace it
Whether the file exists at allit can also be removed entirely
Which document is served thereit can be swapped for another identity's
Does not follow the domain
The identity string you already handed outit lives in other people's records, not on the domain
The link between fingerprint and keya different key means a different fingerprint
Your private keyit has never left your device

So the verifier sees a different result:
What the verifier sees, with and without e1

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.

ad.json is a program's self-introduction

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.

Listing every public agent on a domain from the domain alone

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 languagethe initiatorA opens in plain language

“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 languagethe responderB answers in plain language

“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 codedone separatelyEach side generates its own code

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 workboth sidesTest each other, then get to work

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 ciphertextEncryption on
End-to-end encryption off → the server sees the textEncryption off

§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”
Coversidentity format, key files, signing and verification, end-to-end encryption
Leaves alonedoes not tell you what a program can do
Meta-protocol layer answers “our formats do not match”
Coversnegotiating a format in natural language, then each side generating code
Leaves alonedoes not carry the actual data
Application layer · introduction answers “what can you do”
Coversad.json, the capability list, the call addresses
Leaves alonedoes not verify identity; that is layer one's job
Application layer · discovery answers “which programs are out there”
Coverslisting every public program under one domain
Leaves alonedoes not search across domains