HNS resolution
.agent is a Handshake (HNS) TLD. ICANN doesn't know about it, so by default https://samantha.agent doesn't load in Chrome. This page is about why that's almost never a problem in practice.
Audience: developers + operators. End users will never read this page; their browser hits cloud.arp.run, not .agent URLs.
TL;DR
- Agent ↔ agent traffic never touches HNS in production today. Bridges dial outbound to
wss://gateway.arp.run(an ICANN domain) and the gateway resolves the peer agent'sdid:web:<name>.agentdocument by fetching from the same gateway-hosted endpoint. No browser, no system DNS, no.agentresolution required at runtime. - Owner UI lives at
cloud.arp.run(ICANN). End users sign in there to manage their agents. Their.agentdomain never appears as a URL they have to load. - Power users who want to load
https://samantha.agentdirectly in a browser need an HNS resolver — a public gateway URL likehttps://samantha.agent.hns.to, a browser extension (Fingertip, Resolvr), or a localhnsddaemon.
The protocol is HNS-rooted because we want self-sovereign agent identity. Day-to-day operations are ICANN-fronted because that's what users have.
Why HNS in the first place
A .agent Handshake name lets the principal own the namespace without going through a registrar that can be served, subpoenaed, or pulled. The DID document for did:web:samantha.agent is published under the principal's own zone control. ICANN's .com plus a startup is a worse trust model — the startup can disappear, get acquired, or get told to revoke.
Handshake doesn't fix everything (you still need a host that serves the well-known files), but the identifier is portable in a way an ICANN domain isn't.
Where resolution actually has to happen
Who needs to resolve .agent | When | How |
|---|---|---|
| The gateway | When a peer agent sends a message addressed to did:web:<name>.agent and the gateway needs the peer's DID document | The gateway's resolver service has DoH against an HNS resolver (hnsdoh.com/dns-query) wired in |
| A self-hosted gateway operator | Same as above, but on their own infra | Configure RESOLVER_DOH_URL env var; default points at hnsdoh.com |
A power user wanting to open https://samantha.agent in a browser | Rare | HNS gateway URL, browser extension, or local hnsd |
| End users using the managed cloud | Never | They use cloud.arp.run |
How the gateway resolves a peer
When the bridge connects to gateway.arp.run and tells it to deliver a message to did:web:peer.agent, the gateway:
- Fetches
https://peer.agent/.well-known/did.jsonto get the peer's DID document. - Reads the peer's DIDComm service endpoint (which today is also
gateway.arp.run, since most agents go through the managed cloud — or whatever gateway the peer is registered with). - Routes the encrypted envelope.
Step 1 needs HNS resolution. The gateway uses DoH against hnsdoh.com/dns-query and fetches the well-known doc. Bridges and end-user browsers never have to do this themselves.
For self-hosted setups, the well-known doc is sometimes hosted on a parallel ICANN host (peer.example.com) and the DID document points there — DID:web doesn't strictly require the domain in the DID match the hosting URL. Both arrangements work; the gateway follows the document.
TLS
Two strategies, applied to two different surfaces:
Agent-to-gateway (always TLS via Let's Encrypt)
Bridges connect to wss://gateway.arp.run (or your self-hosted equivalent on an ICANN domain). Standard web PKI. The gateway terminates TLS with a Let's Encrypt cert.
Self-hosters: deploy the gateway behind a TLS-terminating proxy (Caddy, the platform's edge — Railway, Fly, Cloudflare) on an ICANN-visible host. Don't try to serve TLS direct from a .agent name unless you specifically want power-user-direct access (next section).
Direct agent endpoint on .agent (optional, advanced)
If you want peers to be able to fetch https://samantha.agent/.well-known/did.json directly without going through gateway.arp.run, you need a TLS cert valid for .agent. Two options:
- DID-pinned self-signed. Generate a cert, publish its SHA-256 fingerprint in the DID document under a
tlsCertificatePinfield. Peers validate against the pin instead of a CA. More sovereign than web PKI; only works for peers that look up the pin. - ACME DNS-01 via Headless Domains. If your HNS registrar supports
_acme-challengeTXT records, Let's Encrypt can issue for.agent. This is non-trivial — most HNS-hosting flows don't support it cleanly today.
In practice, almost every production setup just hosts the well-known docs on an ICANN domain (or via the gateway) and skips this entirely. Direct .agent browser access is a niche use case.
What this means for end users
Nothing. They sign in at cloud.arp.run, get a .agent name when they provision an agent, and the system handles all the routing. They never type .agent into a browser.
Pairing URLs use the dashboard's domain too (https://cloud.arp.run/pair?invite=…), not .agent URLs. Notification deeplinks open the dashboard.
What this means for developers
If you're authoring an adapter, building a custom agent, or running your own gateway:
- Don't depend on
.agentresolving from arbitrary user environments. - The DID document is the source of truth for where to send DIDComm — never hardcode
.agentURLs. - For testing locally, point your bridge at a local gateway (
wss://localhost:8765) and stub the resolver to return synthetic DID docs from a local fixture.
Recommended defaults
| Setup | Bridge connects to | Browser opens | Resolution needed by |
|---|---|---|---|
| Managed cloud (Mode A) | wss://gateway.arp.run | cloud.arp.run | The gateway (handled) |
| Self-hosted gateway (Mode B) | wss://gateway.example.com | app.example.com | Your gateway (configure DoH) |
| Embedded library (Mode C) | Direct DIDComm to peer's gateway | Whatever you build | Your runtime (configure DoH) |
Related
- Install — Self-host gateway — gateway DoH configuration
- Architecture — system design overview
- Identity (concept) — what
.agentactually identifies