Sender SDK
The @directory/client SDK handles the full resolution flow: DNS discovery, address resolution, Ed25519 verification against every published key, clock-skew-tolerant freshness checks, key-rotation retry, and route matching.
Install
npm install @directory/clientBasic Usage
import { DirectoryClient } from '@directory/client';
const client = new DirectoryClient();
const result = await client.resolve('alice@gmail.com', [ { value_type: 'USDC', transfer_type: 'ethereum' }, { value_type: 'USDC', transfer_type: 'base' },]);
if (result.verification.valid && result.matchedRoutes.length > 0) { const route = result.matchedRoutes[0]; console.log(`Send ${route.value_type} on ${route.transfer_type} to ${route.destination}`);}What the SDK Does
The resolve() call performs these steps automatically:
- Normalize the address (IDN punycode, NFC, whitespace, length checks).
- Discover — DNS-over-HTTPS lookups for
_drs._tcp.<domain>SRV and_drskey.<domain>TXT. All key records are returned; the client tries each during verification. - Optional manifest — fetches
/.well-known/directory.jsonif published; treats404as “no manifest”. - Resolve —
POST /resolvewith{ address }. - Verify — JCS-canonicalize, check Ed25519 signature against each published key, check
issued_at/expires_atwith ±60 s skew. OnINVALID_SIGNATURE, the client refetches DNS keys once and retries to absorb key-rotation overlap. - Match — filter the contract’s routes against the sender’s capabilities.
Step-by-Step (Lower Level)
import { discoverDrs, fetchCapabilities, resolveAddress, verifyContract, matchRoutes,} from '@directory/client';
const { baseUrl, publicKeys } = await discoverDrs('gmail.com');const manifest = await fetchCapabilities(baseUrl); // may be nullconst contract = await resolveAddress(baseUrl, 'alice@gmail.com');
const verification = await verifyContract(contract, publicKeys);if (!verification.valid) throw new Error(verification.reason);
const senderCapabilities = [ { value_type: 'USDC', transfer_type: 'ethereum' },];const routes = matchRoutes(contract.routes, senderCapabilities);Error Handling
import { DrsDiscoveryError, DrsResolveError } from '@directory/client';
try { const result = await client.resolve('alice@unknown.com', capabilities);} catch (err) { if (err instanceof DrsDiscoveryError) { // No DRS found for domain — no SRV record. } if (err instanceof DrsResolveError) { // DRS returned an RFC 7807 Problem Details response. err.status; // 404 err.code; // "ADDRESS_NOT_FOUND" err.message; // detail || title — parsed from the response body }}Verification Outcomes
verification.valid; // booleanverification.reason; // "INVALID_SIGNATURE" | "EXPIRED_CONTRACT" | "NOT_YET_VALID" | undefinedA client that treats valid === false as a hard failure is correct. The SDK has already retried once on signature failure.
Runtime Compatibility
- Node.js 20+ — native Web Crypto API.
- Cloudflare Workers — native Web Crypto API.
- Modern browsers — native Web Crypto API.
Zero external dependencies for cryptographic operations.