Skip to content

Security Model

Directory routes value the same way DNS routes connections and SMTP routes messages. The security model is the same too. The threats are the same. The privacy expectations should be the same.

Trust anchor

DNS is the root of trust. A domain’s operator controls two DNS records:

  • _drs._tcp SRV — where the node lives.
  • _drskey TXT — one or more Ed25519 public keys that sign routing contracts.

If you control these records, you control the routing. If an attacker controls these records, they control the routing. There is no secondary trust layer. This is the same model as MX records for email and A records for web traffic. It is well-understood, battle-tested, and imperfect.

DNSSEC

DNS without DNSSEC is unauthenticated. A cache poisoning attack can substitute a fake public key, stand up a fake node, and sign valid-looking contracts that route to the attacker’s destination. The Ed25519 signature on the routing contract protects against tampering after discovery. DNSSEC protects discovery itself.

DNSSEC is strongly recommended for any domain running a Directory node. See DNS Records.

TLS

HTTPS provides transport security between the client and the node. TLS prevents a network observer from reading or modifying resolve requests and routing contracts in transit.

TLS does not authenticate the node’s identity beyond the certificate. CDNs terminate TLS. Certificates can be misissued. The Ed25519 signature on routing contracts is the protocol’s own authentication — it proves the contract was issued by whoever controls the domain’s private key, regardless of what happens at the transport layer.

Canonicalization

All signed responses are canonicalized with RFC 8785 JSON Canonicalization Scheme (JCS) before signing and before verification:

  • UTF-8 encoding.
  • Lexicographic key ordering on objects.
  • RFC 7159 number serialization (canonical).
  • No insignificant whitespace.
  • undefined values are stripped; non-finite numbers (NaN, ±Infinity) are rejected.

Clients MUST canonicalize with JCS. Any other serialization will produce different bytes and fail verification.

Signing

Routing contracts are signed using Ed25519 (RFC 8032). The signing input is the JCS canonicalization of the contract with the signature field removed. The signature is base64-encoded and placed in the signature field of the response.

The public key lives in DNS (_drskey.<domain>). Operators MAY publish multiple keys to support zero-downtime rotation.

Freshness

issued_at and expires_at MUST be RFC 3339 timestamps. Clients MUST reject:

  • Any contract whose issued_at is more than +60 s in the future.
  • Any contract whose expires_at is more than -60 s in the past.

The ±60 s tolerance absorbs routine clock drift without extending the life of a contract beyond the TTL intended by the operator.

Multi-key verification and rotation

Clients MUST fetch all _drskey TXT records for a domain and attempt verification against each one. A contract is valid if any key verifies it. This lets operators rotate keys without breaking senders in flight:

  1. Publish the new key as an additional _drskey TXT record.
  2. Wait for DNS TTLs to expire.
  3. Start signing with the new key.
  4. Remove the old record once traffic has drained.

On verification failure, clients MUST re-fetch DNS records once and retry — this handles the brief window where a cached key lags a rotation.

Address normalization

Before lookup or signing, addresses MUST be normalized:

  • localpart@domain is split on the last @ (no @ means the whole string is a bare domain).
  • The domain is converted to ASCII punycode (IDN) and lowercased.
  • The local part is Unicode-normalized to NFC, whitespace-trimmed, and (for lookup) lowercased.
  • The local part MUST be <= 64 bytes after NFC; the full domain MUST be <= 253 bytes.

The signed contract’s address field MUST be the normalized form.

Unknown fields

Conforming implementations MUST preserve unknown JSON fields in any request or response and MUST canonicalize and sign them alongside known fields. This preserves forward compatibility: future protocol versions can add fields without invalidating existing verifiers.

Routing contract integrity

Because every routing contract is JCS-canonicalized and Ed25519-signed:

  • A contract cannot be modified in transit without invalidating the signature.
  • A contract cannot be forged without the domain’s private key.
  • A contract can be verified by any third party without contacting the issuing node.
  • An expired contract is rejected even if the signature is valid.

This is DKIM for routing.

What the protocol does not authenticate

The optional capability manifest at /.well-known/directory.json is not signed. It is served over HTTPS and cached. A CDN compromise or cache poisoning could serve a modified manifest.

The manifest is informational — it is a hint about what pairs the node accepts. It does not carry destinations. A tampered manifest could cause a client to believe a node doesn’t support an asset type (denial of service) or to attempt a resolution that fails. It cannot redirect funds.

Client requirement: Clients MUST verify that the domain they queried matches their own resolution target. Do not trust the domain field in the manifest to determine where to resolve.

Privacy

Routing is metadata. When Bob resolves alice@gmail.com, the following parties learn the following things:

WhoWhat they learn
Gmail (the operator)Someone from Bob’s IP intends to pay Alice. Approximate time.
DNS resolverBob’s machine queried _drs._tcp.gmail.com and _drskey.gmail.com.
Network observerWithout DoH/DoT, the DNS queries are visible. The HTTPS connection is visible (contents encrypted).
The blockchainAfter settlement, the destination address and amount are public for on-chain transfers.

This is not unique to Directory. This is how routing works. When Bob sends Alice an email, Gmail sees the envelope. When Bob visits a website, the DNS resolver sees the query. When Bob sends an on-chain transfer, the ledger sees the destination.

Operator responsibility

Operators see resolution requests. Operators SHOULD:

  • Not log resolution requests beyond what is needed for abuse prevention.
  • Not correlate resolution metadata with on-chain activity.
  • Not sell or share resolution data.
  • Rotate destination addresses when possible to limit on-chain correlation.

Destination privacy

If an operator returns the same wallet address for every resolve, all payments to that address are linkable on-chain. Operators who care about recipient privacy should generate unique deposit addresses per resolution. The protocol supports this — the destination field is opaque and can be anything the operator controls.

Key management

The Ed25519 private key is the most sensitive secret in a Directory node. Whoever holds it can sign routing contracts for any address on the domain.

Storage

  • Cloudflare Workers — Worker Secrets (encrypted at rest, not visible in the dashboard).
  • AWS — Secrets Manager or Parameter Store.
  • Self-hosted — environment variable, not committed to source control.

Rotation

See “Multi-key verification and rotation” above. The protocol supports overlap natively; there is no separate rotation ceremony.

Compromise

If the private key is compromised:

  1. Generate a new keypair immediately.
  2. Publish the new public key as an additional _drskey record.
  3. Flip the node’s signing key to the new private key.
  4. Remove the old _drskey record.
  5. Lower DNS TTLs ahead of anticipated rotations.

During the propagation window, the attacker can still sign valid contracts. There is no revocation mechanism beyond DNS propagation. This is the same constraint as DKIM key compromise.

Abuse model

Directory inherits DNS’s trust model, including its abuse handling mechanisms:

  • Domain disputes are handled by ICANN and registrars (UDRP).
  • Phishing domains are handled by registrar abuse teams and browser safe-browsing lists.
  • Spoofing requires control of the target’s DNS records — the same bar as spoofing a website.

An attacker cannot spoof gmail.com routing without compromising Google’s DNS. They can register gmai1.com and run a node — but that’s a domain impersonation problem, handled by the same mechanisms that handle phishing websites today.

Operator-enforced policy (out of band)

The protocol does not specify rate limits, address-enumeration mitigations, logging retention, or access control. These are operator responsibilities, the same way SMTP rate limits are a mail server decision. Operators SHOULD implement them based on their own abuse model. Examples:

  • Rate-limit POST /resolve per source IP.
  • Rate-limit probes that return ADDRESS_NOT_FOUND.
  • Require authenticated access for internal address admin endpoints.
  • Log resolutions with PII-minimizing retention windows.

Request validation (protocol-required)

Implementations MUST:

  • Reject request bodies larger than 4 KiB with 413 PAYLOAD_TOO_LARGE.
  • Reject non-JSON request content with 415 UNSUPPORTED_MEDIA_TYPE.
  • Reject malformed JSON with 400 INVALID_REQUEST.
  • Reject addresses that fail normalization with 400 INVALID_REQUEST.
  • Reserve internal key names (e.g., _root) from user address registration.