HNS resolution
Problem: Handshake (HNS) domains don't resolve natively in standard browsers or operating systems. ICANN's DNS doesn't know about .agent. Without a bridge, typing https://atlas.agent into Chrome fails.
This affects how humans access the owner UI. It does not affect agent-to-agent communication, which uses DoH natively.
This doc explains the split, the solutions, and what each install mode (Mac/tunnel, VPS, Cloud) should do.
1. Two audiences, two resolution paths
| Audience | What they need | Solution |
|---|---|---|
| Agents (bot ↔ bot) | Resolve did:web:ghost.agent, fetch well-known docs, open DIDComm | DoH (https://hnsdoh.com/dns-query) or local hnsd daemon |
| Humans (owner ↔ UI) | Open https://ian.atlas.agent in a browser or mobile app | Mobile app (built-in DoH), HNS gateway URL, or SaaS fallback |
Agent resolution is already a solved problem — the Headless Domains skill doc covers it. Our runtime configures DoH out of the box. No user action required.
Human resolution is the gap. The rest of this doc is about filling it.
2. Agent-side resolution (baked into our runtime)
The ARP sidecar / SDK / cloud client all ship with HNS resolution preconfigured:
Default: DoH against https://hnsdoh.com/dns-query. Zero config.
Advanced: users can run hnsd locally and point the runtime at 127.0.0.1:53 for trustless resolution.
Wire-level, this means when samantha.agent needs to talk to ghost.agent:
samantha's runtime
→ DoH GET hnsdoh.com/dns-query?name=ghost.agent&type=A
→ returns IP
→ HTTPS to that IP, Host: ghost.agent
→ TLS validated against cert pinned in ghost.agent's DID doc (§4)
→ DIDComm envelope delivered
This is a self-contained path. It does not depend on ICANN DNS, the user's system resolver, browser extensions, or anything Let's Encrypt sees.
3. Human-side resolution (three paths)
The owner UI is https://ian.atlas.agent. Three working ways to reach it:
Path A — Mobile app (recommended default)
The ARP mobile app ships with a built-in HNS resolver. When you tap a notification or scan a pairing QR, the app resolves the owner URL internally and connects directly. The browser DNS problem doesn't apply because we don't use the system browser.
This is the primary owner UI for most users. Fast, native, handles biometrics and push notifications.
Path B — HNS gateway URL (works in any browser)
Public HNS gateways translate HNS names into regular .com/.to URLs:
https://ian.atlas.agent ← native HNS (won't load without extension)
https://ian.atlas.agent.hns.to ← works in any browser, anywhere
The gateway does the HNS lookup server-side and proxies the content. TLS is handled by the gateway. Usable from a borrowed laptop, a mobile browser, a work computer — any environment where installing extensions isn't possible.
Cost: the gateway sees your traffic. Not suitable for sensitive sessions, but fine for read-only audit viewing or quick revocations.
Path C — SaaS fallback at app.arp.spec
We host a web app at app.arp.spec (a regular ICANN domain with a real cert). You log in with your principal DID. The SaaS pulls data from your agent via authenticated API and renders the same owner UI.
When to use: first-time setup before your agent is running, recovery if your owner subdomain is misconfigured, access from a locked-down machine.
Cost: we see metadata (who's logged in, what they're looking at). Message bodies stay E2E encrypted between agents — app.arp.spec doesn't get those — but it does know you opened the audit log.
Path D — Browser extension (power users)
Extensions like Fingertip or Resolvr add HNS resolution to Chrome/Firefox. Once installed, https://ian.atlas.agent loads natively. TLS certificate handling depends on the extension (usually by accepting a browser-level CA override or using a local proxy).
When to recommend: technical users who want native HNS browsing across the board. Not the default.
4. TLS strategy
TLS is the other thing HNS affects. Standard web PKI (Let's Encrypt) expects ICANN-visible DNS for validation. .agent is HNS-only, so you can't just certbot your way to a cert.
Two TLS strategies, used in different places:
4.1 Agent ↔ agent: self-sovereign TLS pinned in DID doc
For agent-to-agent traffic (the main protocol), skip web PKI entirely.
The agent's DID document publishes its TLS cert fingerprint:
{
"id": "did:web:atlas.agent",
"verificationMethod": [...],
"service": [
{
"type": "DIDCommMessaging",
"serviceEndpoint": "https://atlas.agent/didcomm",
"tlsCertificatePin": {
"algorithm": "sha256",
"value": "a3f5b2e1c8d4..."
}
}
]
}
Peer agents validate by computing the fingerprint of the presented cert and comparing. The cert can be self-signed — identity comes from the DID, not a CA. This is more secure than web PKI for this use case because there's no CA that could mis-issue, and the fingerprint is under the principal's signature chain.
The ARP SDK handles cert generation, rotation, and DID-doc publishing automatically. Sidecar users don't see any of this.
4.2 Human UI endpoints: regular web PKI
Where humans need to connect from browsers or mobile apps with standard TLS stacks:
| Endpoint | Cert source |
|---|---|
hns.to gateway | Gateway's own wildcard cert (*.hns.to) |
app.arp.spec SaaS | Regular Let's Encrypt on arp.spec |
| Tunnel endpoints (ngrok, Cloudflare) | Tunnel provider's cert |
| ARP Cloud hosted UI | Our Let's Encrypt wildcard |
In every human-facing case, the cert lives on an ICANN-visible domain — so standard LE works.
4.3 The bridge: ACME-over-DNS-01 via Headless
If a future install mode wants to serve HTTPS directly to browsers from an HNS domain (e.g., a power-user setup with a browser extension), ACME DNS-01 can work if Headless exposes a TXT-record API for _acme-challenge.atlas.agent. Let's Encrypt queries HNS via their own resolver (they don't — but there's a workaround: publish the TXT record on both HNS and a parallel ICANN domain, and LE validates the ICANN copy).
This is not v0 territory. Listed for completeness.
5. What each install mode does about it
Mac + tunnel (ngrok / Cloudflare)
Agent traffic: sidecar uses DoH → resolves peer .agent names → connects. Fine.
Inbound: atlas.agent needs to resolve to your tunnel endpoint. Two options:
- ngrok with custom domain: you've already set
atlas.agentas your ngrok custom domain, so HNS and ngrok both point the right way. ngrok's cert covers it (they handle custom-domain certs). Other agents resolveatlas.agentvia DoH → ngrok IP → terminate TLS at ngrok → sidecar. - Cloudflare Tunnel: same pattern.
atlas.agentresolves via HNS; Cloudflare terminates TLS with their cert foratlas.agent(auto-provisioned).
Owner UI: use the mobile app, or ian.atlas.agent.hns.to in a browser, or app.arp.spec as fallback.
VPS
Agent traffic: sidecar runs on VPS, bound to port 443. Peer agents resolve atlas.agent via DoH → VPS IP → direct HTTPS to sidecar.
TLS: the sidecar's auto-ACME won't work out of the box because LE can't resolve atlas.agent via ICANN DNS. Two fixes:
- Use DID-pinned self-signed cert (agent-to-agent default). Sidecar generates a cert, pins its fingerprint in the DID document on first boot. Peer agents validate against the pin. No LE needed.
- Add a parallel ICANN domain (e.g.,
atlas-proxy.arp.spec) pointing to the same IP. LE issues a cert for that. Sidecar serves both names; peer agents connect toatlas.agent(HNS) but the cert also coversatlas-proxy.arp.specvia SAN. Belt-and-suspenders for the rare peer that can't yet validate DID-pinned certs.
Default: option 1 in v0. Option 2 is a migration path if we find old runtimes need web PKI.
Owner UI: same three paths — mobile app, hns.to gateway, or SaaS.
ARP Cloud
Agent traffic: fully handled on our side. We run hnsd + DoH. Peers resolving atlas.agent get ARP Cloud's IP, terminate TLS against our cert (LE-issued for the ICANN domain we front .agent domains on), we proxy to your Mac's outbound connection.
Owner UI: we serve ian.atlas.agent content directly from our infra. The URL uses the HNS name for routing (we control the zone on the HNS side too), but the public-facing endpoint is on our ICANN-visible infrastructure with a normal cert. Browser loads it because we've bridged the names for you.
Our fallback: if your browser can't resolve HNS, it auto-redirects to the equivalent app.arp.spec/atlas.agent/owner URL and you never notice.
This is the mode where the HNS-resolution problem is least visible to users.
6. Recommended defaults per user type
| User type | Owner UI path | Agent TLS |
|---|---|---|
| Non-technical | Mobile app → app.arp.spec fallback | Managed by us (Cloud mode) or sidecar (VPS/local) |
| Technical, self-hosted | Mobile app → ian.atlas.agent.hns.to | DID-pinned self-signed, automatic via sidecar |
| Power user | Browser extension → native HNS | DID-pinned or LE-via-DNS-01 |
| Enterprise | SaaS + SSO to app.arp.spec | Managed, with audit requirements |
7. What this changes in the existing docs
| Doc | Change |
|---|---|
ARP-architecture.md | Layer 0 description gains a note: HNS-native resolution for agents via DoH, mixed strategy for humans |
ARP-tld-integration-spec*.md | §3.3 TLS — specify that Headless should expose DNS-01 challenge support if we need LE for hybrid use cases, but v0 can run on DID-pinned TLS for agent endpoints |
ARP-installation-and-hosting.md | Add a short section noting that every install mode defaults to DID-pinned TLS for agent traffic; humans use mobile/gateway/SaaS |
ARP-example-atlas-*.md (×3) | Add a short "Accessing the owner UI" note describing the three browser paths |
Edits to the example docs happen now.
8. Summary — the one paragraph
HNS domains don't resolve in standard browsers, but this only affects how humans reach the owner UI. Agents talk to each other using DoH against hnsdoh.com/dns-query or a local hnsd daemon — zero user-visible friction, all wired up in the ARP runtime. Humans reach the owner UI via the ARP mobile app (built-in resolver), an HNS gateway URL like ian.atlas.agent.hns.to, or our SaaS fallback at app.arp.spec. TLS for agent-to-agent traffic uses certificate fingerprints pinned in the DID document (more sovereign than web PKI); TLS for human-facing UIs uses regular Let's Encrypt on ICANN-visible domains. The architecture stays clean because the two audiences are solved separately.
Source: docs/ARP-hns-resolution.md · Ported 2026-04-23