Operator Quickstart
An operator runs a DRS node for a domain. If you control mycompany.com in DNS, you can run a DRS for all *@mycompany.com addresses.
Prerequisites
- A domain with DNS managed by Cloudflare
- A Cloudflare API token with Zone:DNS:Edit, Workers:Edit, Account:Workers KV:Edit
- Node.js 20+ and pnpm
1. Initialize
drs init --domain mycompany.com --cf-token <token>This:
- Generates an Ed25519 keypair
- Creates KV namespaces for addresses and config
- Creates DNS records:
_drs._tcp.mycompany.comSRV and_drskey.mycompany.comTXT - Enables DNSSEC on the zone
- Writes initial config to KV
Save the private key output — you’ll need it in the next step.
2. Store the signing key
cd packages/drs-workerwrangler secret put DRS_SIGNING_KEY --env mycompany# Paste the base64 private key when prompted3. Configure wrangler.toml
Add an environment for your domain in packages/drs-worker/wrangler.toml:
[env.mycompany]vars = { DOMAIN = "mycompany.com", NODE_TYPE = "standard" }routes = [{ pattern = "mycompany.com/*", zone_name = "mycompany.com" }]
[[env.mycompany.kv_namespaces]]binding = "ADDRESSES"id = "<addresses-kv-id-from-init>"
[[env.mycompany.kv_namespaces]]binding = "CONFIG"id = "<config-kv-id-from-init>"4. Configure accepted pairs
drs update-config --accepts '[ {"value_type":"USDC","transfer_type":"ethereum", "reference":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "sponsor":"circle.com"}, {"value_type":"ETH","transfer_type":"ethereum"}]' --cf-token <token> --account-id <id> --kv-namespace <config-id>5. Add addresses
drs add-address alice --routes '[ {"value_type":"USDC","transfer_type":"ethereum","destination":"0xABC..."}, {"value_type":"ETH","transfer_type":"ethereum","destination":"0xABC..."}]' --cf-token <token> --account-id <id> --kv-namespace <addresses-id>6. Deploy
cd packages/drs-workerwrangler deploy --env mycompany7. Verify
# DNS is configured correctlydrs check-dns mycompany.com
# Manifest is livecurl https://mycompany.com/.well-known/directory.json | jq
# Resolution workscurl -s -X POST https://mycompany.com/resolve \ -H 'Content-Type: application/json' \ -d '{"address":"alice@mycompany.com"}' | jq
# Full end-to-end test (resolve + signature verification)drs test-resolve alice@mycompany.comMaking your domain payable
The domain itself can be a payable address. Resolving mycompany.com pays the company. Resolving alice@mycompany.com pays Alice. Both work on the same node.
The domain’s default route is stored under the reserved _root key:
drs add-address _root --routes '[ {"value_type":"USDC","transfer_type":"ethereum","destination":"0xCOMPANY_TREASURY..."}, {"value_type":"USD","transfer_type":"ach","destination":"021000021:999999999"}]' --cf-token <token> --account-id <id> --kv-namespace <addresses-id>Then set payable: true and an admin contact in your node config so the manifest advertises it:
{ "payable": true, "admin": "admin@mycompany.com"}Now mycompany.com is a valid resolve target:
curl -s -X POST https://mycompany.com/resolve \ -H 'Content-Type: application/json' \ -d '{"address":"mycompany.com"}' | jqManaging addresses with YAML files
For more than a handful of addresses, use YAML files instead of CLI flags. Create a directory of address files and sync them in one command:
drs sync --dir ./addresses --cf-token <token> --account-id <id> --kv-namespace <addresses-id>Each file is named after the local part of the address. The filename becomes the address — alice.yaml becomes alice@yourdomain.com.
Basic address
status: activeroutes: - value_type: USDC transfer_type: ethereum destination: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" - value_type: ETH transfer_type: ethereum destination: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"Multi-chain address
A user who accepts USDC on three chains:
status: activeroutes: - value_type: USDC transfer_type: ethereum destination: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B" - value_type: USDC transfer_type: base destination: "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18" - value_type: USDC transfer_type: solana destination: "7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV"Fiat-only address
A traditional business that only accepts bank transfers:
status: activeroutes: - value_type: USD transfer_type: ach destination: "021000021:123456789" - value_type: USD transfer_type: wire destination: "CHASUS33:123456789"Mixed crypto + fiat
status: activeroutes: - value_type: USDC transfer_type: ethereum destination: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" - value_type: USD transfer_type: ach destination: "021000021:987654321" - value_type: BTC transfer_type: bitcoin destination: "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"Retired address
An address that was once active but is no longer accepting transfers. Resolves to a 404:
status: retiredroutes: []Directory structure
A typical node directory looks like:
addresses/├── alice.yaml├── treasury.yaml├── pay.yaml├── accounting.yaml└── old-treasury.yamlRun drs sync whenever you add, update, or retire addresses. The command uploads every file in the directory to KV — it’s idempotent.
What you control
As an operator, you decide:
- Which pairs to accept — USDC on Ethereum, USD via ACH, BTC — whatever your infrastructure supports
- What destinations to return — permanent wallets, rotating proxies, temporary deposit addresses, payment endpoints
- Contract TTL — how long routing contracts are valid (30–3600 seconds)
- Internal delivery — what happens after the sender follows the routing contract is entirely your business
The protocol gives you a standard interface. What you build behind it is up to you.