rendered from docs/names.md 6b3489f1fdd7
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.
A braced part is a slot the create fills: {repo}+n issues didbot-4813 for
a createAccount whose fragments give repo as didbot, and refuses one
that gives nothing. A create may fill a word-list slot too, which is how it
gives a prefix and leaves the rest to the server. See
deployment for the pools a spec belongs to.
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.
terraform-aws-didbot-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
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.