Identities for entities did.bot
agent llm did
didbot docs names.md
14 kB
Markdown
at main

The names an agent has #

An account is two hostnames, a DID document and a plain-text DID. None of that is a DNS record. This page works one example all the way through: what exists in the zone, what the server answers, and what a stranger reads to get from a name to a repository.

Deployment states the rules this example obeys; the shape of the system draws the hosts around it.

The example #

A zone pds.example, delegated to one server at 203.0.113.10. One account, minted with a namer, so it has both hostnames a deployment can issue: the opaque one the DID is minted from, and the issued handle.

zone pds.example
DID did:web:7f3a9c.pds.example
handle basalt-kestrel.pds.example
PDS endpoint https://pds.example

A deployment without a namer has one hostname per account rather than two: the handle is then the DID's own host, which Provisioner::issued_handle reports as no issued handle at all, because there is nothing extra to resolve or withdraw.

A namer draws its name from word lists, and a spec may add n for the deployment's monotonic counter: mineral+creature+n issues basalt-kestrel-4813. One counter feeds every template a deployment configures, so changing the spec keeps counting rather than starting over, and no count is ever issued twice. What the label then discloses is the count itself — how many accounts this deployment has minted, and in what order. t is the other such part: it stamps the current unix time in whole seconds, so mineral+creature+t issues basalt-kestrel-1790000000 and the label discloses when the account was minted, to the second. Nothing about the zone changes; a counted or stamped handle resolves through the same wildcard as any other.

Everything that exists in DNS #

Three names, and none of them is per-account:

pds.example.                    NS    ns-…                (in the parent zone,
                                                           added by hand)
pds.example.                    A     203.0.113.10
*.pds.example.                  A     203.0.113.10
_acme-challenge.pds.example.    TXT   "<token>" "<token>"  (only while a
                                                            certificate is
                                                            being issued)

Minting an account writes nothing here. 7f3a9c.pds.example and basalt-kestrel.pds.example resolve because *.pds.example synthesizes an answer for them, and they do so the instant the name is chosen — there is no propagation to wait for and no external call on the provisioning path.

infra/pds/route53.tf writes the wildcard beside the apex, both at the instance's address. didbot-dns is the zone's writer for the two records a wildcard cannot stand in for — the _acme-challenge TXT values below and the apex CAA — and nothing else in the zone changes after tofu apply. Deployment says what the credential that writes those can reach.

Everything the server answers #

DNS ZONE · pds.example · external pds.example A · 203.0.113.10 *.pds.example A · 203.0.113.10 _acme-challenge.pds.example TXT · two values at once, only during issuance three records, whatever the account count provisioning writes none of them the NS delegation lives in the parent, by hand SYNTHESIZED — no node in the zone 7f3a9c.pds.example 203.0.113.10 basalt-kestrel.pds.example 203.0.113.10 any depth, while no closer node exists — publishing one real record here would end that resolves ONE LISTENER · 203.0.113.10 certificate: pds.example + *.pds.example a TLS wildcard matches exactly one label — unlike the DNS one dispatch on the Host header one process, every account; the name is the whole key 7f3a9c.pds.example the DID's own hostname GET /.well-known/did.json application/json id · alsoKnownAs · #atproto · #atproto_pds 404 for a name no account holds basalt-kestrel.pds.example the issued handle GET /.well-known/atproto-did text/plain did:web:7f3a9c.pds.example answers only when the document agrees WHAT did.json SAYS · the four fields atproto reads id did:web:7f3a9c.pds.example alsoKnownAs at://basalt-kestrel.pds.example verificationMethod #atproto · Multikey service #atproto_pds · https://pds.example the rest of the W3C surface is not emitted, because nothing reads it points back at the same account WHAT A STRANGER DOES 1 · handle → GET /.well-known/atproto-did → a DID 2 · DID → GET /.well-known/did.json on its own host 3 · check id, and alsoKnownAs back to the handle 4 · #atproto_pds → the XRPC surface no step consults DNS for anything but reachability

Both hostnames land on the same listener, and the Host header is the only thing that says which account a request means. did_json and atproto_did in didbot-serve's routes both read it, and both derive their answer from one registry, so the bidirectional check holds by construction: a resolver following the handle to the DID and the DID to its document always finds the handle again in alsoKnownAs.

A name no account holds is a 404 rather than an NXDOMAIN, because the wildcard resolves it and the TLS handshake completes. That is a conformant answer — a resolver treats any non-200 as unresolvable — and it is the only one that can express a decommissioned account, which keeps serving its document and its verification method while its repository is gone.

What the wildcard does not cover #

The apex. *.pds.example does not answer for pds.example. That is a separate record, and the reason deployment keeps agents off the apex in the first place.

Any type that is not at the wildcard node. A wildcard holding A answers NODATA for a TXT query at a synthesized name. Nothing wildcards the ACME DNS-01 challenge: the authority asks for a literal token at _acme-challenge.<name>, so a deployment issuing its own certificates needs a provider that writes real records, and needs two values present at one name simultaneously to cover both the apex and the wildcard in one order.

Anything below a name that exists. DNS wildcards synthesize at any depth, but only while no closer node exists — publish a real record at basalt-kestrel.pds.example and x.basalt-kestrel.pds.example stops resolving, because the lookup now wants *.basalt-kestrel.pds.example and finds nothing. The zone holds no per-account record, so nothing shadows the wildcard; the TLS certificate rules out the deep names anyway.

DNS wildcards and TLS wildcards are not the same rule #

They are easy to conflate and they differ where it matters:

matches
*.pds.example in DNS any depth — a.pds.example, a.b.pds.example — while no closer node exists
*.pds.example in a certificate exactly one label, and never the bare parent

So the constraint on how deep an agent hostname may sit is the certificate, not the zone. No authority issues a multi-label wildcard, which is why a deployment with two zones holds two certificates and didbot_tls::fleet::CertificateFleet keeps one manager per zone.