DNS Records
Directory uses standard DNS record types that work with any DNS provider. The drs CLI automates this for Cloudflare, but you can create these records manually on any provider.
Service Discovery — SRV
Locates the DRS for a domain. Analogous to MX records for email.
_drs._tcp.yourcompany.com SRV 10 5 443 yourcompany.com| Field | Type | Description |
|---|---|---|
priority | integer | Lower values preferred (same as MX) |
weight | integer | Relative weight for same-priority records |
port | integer | Always 443 (HTTPS only) |
target | string | Hostname of the DRS |
Client behavior:
- Query
_drs._tcp.{domain}for SRV records - Select target using standard SRV priority/weight algorithm (RFC 2782)
- Open HTTPS connection to the selected target
Public Key Distribution — TXT
Publishes the Ed25519 public key for routing contract verification. This is DKIM for value routing.
_drskey.yourcompany.com TXT "v=drs1; k=ed25519; p=BASE64PUBLICKEY"| Field | Type | Description |
|---|---|---|
v | string | Protocol version. Always drs1. |
k | string | Key type. Always ed25519. |
p | string | Base64-encoded Ed25519 public key (raw 32 bytes) |
Parsing rules:
- Fields are semicolon-delimited key=value pairs
- Whitespace around delimiters is ignored
vmust equaldrs1,kmust equaled25519pmust be valid base64 decoding to exactly 32 bytes
Client behavior:
- Retrieve key before verifying any routing contract
- Cache for DNS TTL only
- On signature verification failure, re-fetch once (handles key rotation)
Notification Opt-in — TXT
Declares that a node sends failed transfer notification emails.
_drs-notify.yourcompany.com TXT "v=drs1; notify=true"When a sender’s node resolves a recipient domain and finds no DRS SRV record, it may send an email notification to the recipient address (since Directory addresses are valid email addresses). The _drs-notify record lets recipients verify notification legitimacy:
- Confirm the email is DKIM-signed by the sender’s domain
- Query
_drs-notify.{sender_domain}for the TXT record
DNSSEC
Directory’s trust model starts at DNS. The SRV record tells senders where to connect. The TXT record tells them which public key to trust. If an attacker poisons either record, they control the routing.
DNSSEC is strongly recommended for any domain running a Directory node.
Without DNSSEC, a DNS cache poisoning attack can substitute a fake public key in _drskey, stand up a node at a fake SRV target, and sign contracts with the attacker’s key. The client verifies the signature — it passes — and sends to the wrong destination. The Ed25519 signature protects against tampering after discovery. DNSSEC protects discovery itself.
Enabling DNSSEC
Most DNS providers support DNSSEC. It requires two steps:
- Enable DNSSEC signing in your DNS provider’s dashboard
- Add the DS record to your domain registrar (your registrar provides instructions)
| Provider | DNSSEC Support |
|---|---|
| Cloudflare | One-click enable in DNS settings |
| AWS Route 53 | Supported via KMS signing keys |
| Google Cloud DNS | Supported via DNSSEC settings |
| GoDaddy | Supported for most TLDs |
Verify with:
dig +dnssec _drskey.yourcompany.com TXTA valid DNSSEC response includes RRSIG records alongside the answer.
Client behavior
Clients SHOULD validate DNSSEC when available. When DNSSEC validation is not possible (e.g., in browser environments), clients MUST still verify the Ed25519 contract signature and SHOULD warn users if the domain lacks DNSSEC.
Creating Records by Provider
Any provider (manual)
First, generate your Ed25519 keypair:
# Using the DRS crypto librarynode -e " import('@directory/crypto').then(async (c) => { const keys = await c.generateKeyPairBase64(); console.log('Public key:', keys.publicKey); console.log('Private key:', keys.privateKey); });"Then create two DNS records in your provider’s dashboard:
Record 1 — SRV
| Setting | Value |
|---|---|
| Type | SRV |
| Name | _drs._tcp |
| Priority | 10 |
| Weight | 5 |
| Port | 443 |
| Target | yourcompany.com |
| TTL | 3600 (or auto) |
Record 2 — TXT
| Setting | Value |
|---|---|
| Type | TXT |
| Name | _drskey |
| Value | v=drs1; k=ed25519; p=YOUR_BASE64_PUBLIC_KEY |
| TTL | 3600 (or auto) |
Cloudflare
The drs CLI handles this automatically:
drs init --domain yourcompany.com --cf-token <token>AWS Route 53
# SRV recordaws route53 change-resource-record-sets --hosted-zone-id <zone-id> --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "_drs._tcp.yourcompany.com", "Type": "SRV", "TTL": 3600, "ResourceRecords": [{"Value": "10 5 443 yourcompany.com."}] } }]}'
# TXT recordaws route53 change-resource-record-sets --hosted-zone-id <zone-id> --change-batch '{ "Changes": [{ "Action": "CREATE", "ResourceRecordSet": { "Name": "_drskey.yourcompany.com", "Type": "TXT", "TTL": 3600, "ResourceRecords": [{"Value": "\"v=drs1; k=ed25519; p=YOUR_BASE64_PUBLIC_KEY\""}] } }]}'GoDaddy / Namecheap / other registrars
Most registrar DNS dashboards support SRV and TXT records. Look for “Advanced DNS” or “DNS Management” in your dashboard, and create the two records using the values from the table above. The field names vary by provider, but the values are always the same.
Verifying your records
After creating the records, verify with:
# Check the SRV recorddig _drs._tcp.yourcompany.com SRV
# Check the TXT recorddig _drskey.yourcompany.com TXT
# Or use the DRS CLIdrs check-dns yourcompany.comHosting the DRS
The DNS records point senders to your DRS. The DRS itself can run anywhere that serves HTTPS:
| Platform | How |
|---|---|
| Cloudflare Workers | wrangler deploy — the reference implementation |
| Any Node.js host | Run the Hono app with @hono/node-server or the included dev server |
| Docker / VPS | Wrap the Node.js server in a container behind nginx/caddy with TLS |
| Vercel / Fly.io / Railway | Deploy the Hono app as a standard Node.js HTTP server |
The only requirement is that the host serves HTTPS on port 443 at the domain specified in your SRV record’s target. The DRS is a stateless HTTPS API — it works anywhere you can run a web server.
For non-Cloudflare deployments, you bring your own storage backend. The DRS defines a KVStore interface:
interface KVStore { get(key: string): Promise<string | null>; put(key: string, value: string): Promise<void>; delete?(key: string): Promise<void>; list?(): Promise<{ keys: { name: string }[] }>;}Cloudflare KVNamespace satisfies this natively. For other platforms, implement get and put against Redis, Postgres, SQLite, DynamoDB — whatever you have. A MemoryKV adapter ships with the reference implementation for local development and testing.