Docs · Reference
Record conventions.
Every record this system writes or reads, with its exact owner name and value syntax. If a convention is designed but nothing publishes it, that is stated in the same row rather than left for you to discover.
At a glance
| Owner | Type | Value | Status |
|---|---|---|---|
_hns.<label> | TXT | v=hns1;pubkey=<64 hex>;epoch=<n>;receipt=<ts>.<b64url sig> |
written and read |
_atproto.<label> | TXT | v=hns1;did=<did>;epoch=<n>;receipt=<ts>.<b64url sig> |
written and read |
_nostr.<name> | TXT | v=nostr1;pubkey=<64 hex>;epoch=<n>;relay=wss://… |
read path only — nothing publishes it |
_443._tcp[.<label>] | TLSA | 3 1 1 <64 hex> | written and read |
<56 hex>._openpgpkey | OPENPGPKEY | base64 of the key packet | mail flow, operator-run step |
<56 hex>._smimecert | SMIMEA | 3 0 1 <64 hex> |
mail flow, operator-run step |
<label> or @ | TXT | ipfs=<cid> / ar=<txid> |
the site pointer |
<label> or @ | TXT | redirect=<https url>[;code=301|302|307|308][;path=drop|append] |
served by the gateway |
_hnsone.<name> | TXT | verification=<token> |
ownership challenge |
_hns, not _nostr
Several comments in our own source still say _nostr where
the code writes _hns. The constant is _hns,
every writer and reader uses it, and querying _nostr under
a live name today returns no data. If you are implementing against
this, follow the table, not the stale comment.
_hns — the control record
One TXT record at _hns.<label>, or at bare
_hns for a name's apex. There is exactly one per owner name;
publishing replaces rather than appends.
_hns.matt.w3. 300 TXT "v=hns1;pubkey=17bab1c31e04...9974;epoch=1;receipt=1787411566.xN8oG3z7...AoQ"
v | always hns1; a reader that
does not see this prefix must treat the record as absent |
pubkey | the control key, 64 lowercase hex (x-only secp256k1) |
epoch | increments when the name changes hands; an older epoch is stale, not merely different |
receipt | <created_at>.<sig>
— Unix seconds, a dot, then 64 signature bytes as unpadded
base64url |
The apex form of the record omits the receipt, because it is written when a control key is bound to a name we already know the owner of rather than by a claim. The whole value must fit in one 255-byte TXT string; publishing refuses rather than splitting it.
The identity receipt
The receipt is not a document served at a URL. It is a signature over a reconstructable event, so that anyone can verify a name's binding to a key without asking us anything.
The signed message is the NIP-01 event id of this event — rebuilt by the verifier, never trusted as transmitted:
{
"pubkey": "<64 hex control key>",
"created_at": 1787411566,
"kind": 30078,
"tags": [["v","hns1"], ["d","hns:matt.w3"], ["epoch","1"]],
"content": ""
}
The atproto receipt is the same shape with
["d","hns:atproto:<name>"] and an extra
["did", "<did>"] tag. The differing d
prefix is what stops one receipt being replayed as the other.
Verifying one, with no trust in us
Read the record
Fetch
_hns.<name> TXT and require the
v=hns1; prefix. A duplicated field key means tampering —
reject rather than take the first.
Decode the receipt
Split on the first dot.
Left is created_at; right is base64url that must decode to
exactly 64 bytes.
Rebuild and verify
Serialise the event as
[0,pubkey,created_at,kind,tags,content] with no
whitespace, SHA-256 it, and verify the signature as BIP-340 Schnorr
over that hash with the record's own pubkey.
No relay, no DNS trust, no call to us. The registry's half of the attestation is separate: it is the DNSSEC signature over that TXT record.
Today _hns and _atproto records are answered
by our nameservers on the unsigned path and do not appear in the signed
zone data. So the self-verifying half — the receipt — works fully, and
the registry half does not yet: a validating resolver cannot prove the
record came from the zone.
The signing queue for these exists and the operator-side drainer has never been run against the live edge. Until it is, verify the receipt yourself rather than relying on the DNSSEC chain for these two record types. Encryption-key records are signed today; these are not.
Nostr
Two mechanisms, and only one of them is live.
- NIP-05 over HTTPS — liveServed at
https://<name>.hns.one/.well-known/nostr.json. Only the local part_resolves; any other name returns an empty answer rather than an error, which is what NIP-05 clients expect. _nostrin DNS — designed, parsed, never publishedThe resolver library can read av=nostr1;record and validate it up to the on-chain DS. Nothing writes one. Querying it under a live name returns no data.
curl "https://matt.w3.hns.one/.well-known/nostr.json?name=_"
{"names": {"_": "<64 hex>"}, "relays": {"<64 hex>": ["wss://...", ...]}}
Up to four relay hints are returned. The route is served ahead of the site pointer deliberately, so a name's own published content can never answer for its identity.
The signed _hns record carries the control key, while
NIP-05 serves the social key. Nothing published today proves the two
belong to the same person. Closing that is known work, not something
you should read into the current records.
Also: name@hns.one as a NIP-05 address does not resolve —
hns.one itself serves no nostr.json, and its
unknown paths answer 200 with an HTML page. The form that works is
_@<name>.hns.one.
atproto
The handle binding that Bluesky and other atproto clients actually read is the HTTPS well-known, not a TXT record:
curl https://matt.w3.hns.one/.well-known/atproto-did
did:plc:uebb7mahovn3yxovcq7lyupq
Bare DID, text/plain, 404 when the name is unbound — never
an empty 200. It is served only after the gateway has read the control
key from the _hns record, read the DID and epoch from the
_atproto record, and verified the receipt. Any failure means
no answer.
The _atproto TXT we publish is not the atproto-spec
did=<did> record. It is our own signed form. We
deliberately do not publish the spec's DNS form under
hns.one: doing so creates an empty non-terminal that stops
the wildcard synthesising, which would take the parent name's own
gateway address offline. That has happened here twice.
/.well-known/did.json is disabled and returns 404
A did:web document would have to carry a verification
method and a service endpoint, and the receipt does not sign either of
those — they would come from an unsigned local file, where a stray
write is a full identity takeover. Rather than ship a document weaker
than it looks, the route answers 404 unconditionally. Do not build
against did:web here.
Fediverse handles
These use no DNS records at all — they are WebFinger and ActivityPub on
the hns.one apex.
curl "https://hns.one/.well-known/webfinger?resource=acct:matt_w3@hns.one"
Note the underscore. The fediverse server we run accepts only
[a-z0-9_] in a username, so dots in a name are mapped to
underscores for the handle: matt.w3 becomes
@matt_w3@hns.one. Interfaces still display the native dotted
name; the underscored form is what other instances resolve.
Naming rules
A label is [a-z0-9]([a-z0-9-]{0,61}[a-z0-9])? — one label,
lowercase, no leading or trailing hyphen. Input is normalised (trimmed,
lowercased, stripped of dots) before it is verified, not after;
the reverse order once let a name be verified in one spelling and stored
in another.
Reserved labels, refused to everyone: the service set —
www mail email ns ns1 ns2 ns3 ns4 mx smtp imap jmap pop pop3
webmail autoconfig autodiscover registry api app gateway dns ipfs arweave
hns odoh query pds relay proof status docs ops social openpgpkey —
plus the role set — hostmaster postmaster webmaster admin
administrator root official support help abuse security. Anything
beginning xn-- is also refused.
Addresses are dotted
A claimed label under a name is addressed as
<label>.<name>.hns.one. A flat
<label>-<name>.hns.one form also resolves and is
still handed out for zones whose certificate wiring is not in place —
because a TLS wildcard matches exactly one label, so the dotted URL would
die in the handshake before any of this mattered. Once a zone is wired,
the dotted form is the address.
Records you cannot edit
Locked to the flow that manages them, for everyone: owner names
beginning _hns or _atproto; the types
OPENPGPKEY and SMIMEA anywhere, which the mail
encryption flow writes as an operator-run step; and
_443._tcp* TLSA in zones with DANE enabled,
where every certificate rollover would overwrite a manual edit.
The zone's apex NS and DS are not editable
either — they live on-chain and are changed in your wallet. See
Delegate.