Skip to content

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
FieldTypeDescription
priorityintegerLower values preferred (same as MX)
weightintegerRelative weight for same-priority records
portintegerAlways 443 (HTTPS only)
targetstringHostname of the DRS

Client behavior:

  1. Query _drs._tcp.{domain} for SRV records
  2. Select target using standard SRV priority/weight algorithm (RFC 2782)
  3. 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"
FieldTypeDescription
vstringProtocol version. Always drs1.
kstringKey type. Always ed25519.
pstringBase64-encoded Ed25519 public key (raw 32 bytes)

Parsing rules:

  • Fields are semicolon-delimited key=value pairs
  • Whitespace around delimiters is ignored
  • v must equal drs1, k must equal ed25519
  • p must 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:

  1. Confirm the email is DKIM-signed by the sender’s domain
  2. 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:

  1. Enable DNSSEC signing in your DNS provider’s dashboard
  2. Add the DS record to your domain registrar (your registrar provides instructions)
ProviderDNSSEC Support
CloudflareOne-click enable in DNS settings
AWS Route 53Supported via KMS signing keys
Google Cloud DNSSupported via DNSSEC settings
GoDaddySupported for most TLDs

Verify with:

Terminal window
dig +dnssec _drskey.yourcompany.com TXT

A 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:

Terminal window
# Using the DRS crypto library
node -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

SettingValue
TypeSRV
Name_drs._tcp
Priority10
Weight5
Port443
Targetyourcompany.com
TTL3600 (or auto)

Record 2 — TXT

SettingValue
TypeTXT
Name_drskey
Valuev=drs1; k=ed25519; p=YOUR_BASE64_PUBLIC_KEY
TTL3600 (or auto)

Cloudflare

The drs CLI handles this automatically:

Terminal window
drs init --domain yourcompany.com --cf-token <token>

AWS Route 53

Terminal window
# SRV record
aws 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 record
aws 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:

Terminal window
# Check the SRV record
dig _drs._tcp.yourcompany.com SRV
# Check the TXT record
dig _drskey.yourcompany.com TXT
# Or use the DRS CLI
drs check-dns yourcompany.com

Hosting the DRS

The DNS records point senders to your DRS. The DRS itself can run anywhere that serves HTTPS:

PlatformHow
Cloudflare Workerswrangler deploy — the reference implementation
Any Node.js hostRun the Hono app with @hono/node-server or the included dev server
Docker / VPSWrap the Node.js server in a container behind nginx/caddy with TLS
Vercel / Fly.io / RailwayDeploy 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.