# Patterns ## Secret Rotation Zero-downtime rotation with versioned naming (`api_key_v1`, `api_key_v2`): ```typescript interface Env { PRIMARY_KEY: { get(): Promise }; FALLBACK_KEY?: { get(): Promise }; } async function fetchWithAuth(url: string, key: string) { return fetch(url, { headers: { "Authorization": `Bearer ${key}` } }); } export default { async fetch(request: Request, env: Env): Promise { let resp = await fetchWithAuth("https://api.example.com", await env.PRIMARY_KEY.get()); // Fallback during rotation if (!resp.ok && env.FALLBACK_KEY) { resp = await fetchWithAuth("https://api.example.com", await env.FALLBACK_KEY.get()); } return resp; } } ``` Workflow: Create `api_key_v2` → add fallback binding → deploy → swap primary → deploy → remove `v1` ## Encryption with KV ```typescript interface Env { CACHE: KVNamespace; ENCRYPTION_KEY: { get(): Promise }; } async function encryptValue(value: string, key: string): Promise { const enc = new TextEncoder(); const keyMaterial = await crypto.subtle.importKey( "raw", enc.encode(key), { name: "AES-GCM" }, false, ["encrypt"] ); const iv = crypto.getRandomValues(new Uint8Array(12)); const encrypted = await crypto.subtle.encrypt( { name: "AES-GCM", iv }, keyMaterial, enc.encode(value) ); const combined = new Uint8Array(iv.length + encrypted.byteLength); combined.set(iv); combined.set(new Uint8Array(encrypted), iv.length); return btoa(String.fromCharCode(...combined)); } export default { async fetch(request: Request, env: Env): Promise { const key = await env.ENCRYPTION_KEY.get(); const encrypted = await encryptValue("sensitive-data", key); await env.CACHE.put("user:123:data", encrypted); return Response.json({ ok: true }); } } ``` ## HMAC Signing ```typescript interface Env { HMAC_SECRET: { get(): Promise }; } async function signRequest(data: string, secret: string): Promise { const enc = new TextEncoder(); const key = await crypto.subtle.importKey( "raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"] ); const sig = await crypto.subtle.sign("HMAC", key, enc.encode(data)); return btoa(String.fromCharCode(...new Uint8Array(sig))); } export default { async fetch(request: Request, env: Env): Promise { const secret = await env.HMAC_SECRET.get(); const payload = await request.text(); const signature = await signRequest(payload, secret); return Response.json({ signature }); } } ``` ## Audit & Monitoring ```typescript export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const startTime = Date.now(); try { const apiKey = await env.API_KEY.get(); const resp = await fetch("https://api.example.com", { headers: { "Authorization": `Bearer ${apiKey}` } }); ctx.waitUntil( fetch("https://log.example.com/log", { method: "POST", body: JSON.stringify({ event: "secret_used", secret_name: "API_KEY", timestamp: new Date().toISOString(), duration_ms: Date.now() - startTime, success: resp.ok }) }) ); return resp; } catch (error) { ctx.waitUntil( fetch("https://log.example.com/log", { method: "POST", body: JSON.stringify({ event: "secret_access_failed", secret_name: "API_KEY", error: error instanceof Error ? error.message : "Unknown" }) }) ); return new Response("Error", { status: 500 }); } } } ``` ## Migration from Worker Secrets Change `env.SECRET` (direct) to `await env.SECRET.get()` (async). Steps: 1. Create in Secrets Store: `wrangler secrets-store secret create --name API_KEY --scopes workers --remote` 2. Add binding to `wrangler.jsonc`: `{"binding": "API_KEY", "store_id": "abc123", "secret_name": "api_key"}` 3. Update code: `const key = await env.API_KEY.get();` 4. Test staging, deploy 5. Remove old: `wrangler secret delete API_KEY` ## Sharing Across Workers Same secret, different binding names: ```jsonc // worker-1: binding="SHARED_DB", secret_name="postgres_url" // worker-2: binding="DB_CONN", secret_name="postgres_url" ``` ## JSON Secret Parsing Store structured config as JSON secrets: ```typescript interface Env { DB_CONFIG: { get(): Promise }; } interface DbConfig { host: string; port: number; username: string; password: string; } export default { async fetch(request: Request, env: Env): Promise { try { const configStr = await env.DB_CONFIG.get(); const config: DbConfig = JSON.parse(configStr); // Use parsed config const dbUrl = `postgres://${config.username}:${config.password}@${config.host}:${config.port}`; return Response.json({ connected: true }); } catch (error) { if (error instanceof SyntaxError) { return new Response("Invalid config JSON", { status: 500 }); } throw error; } } } ``` Store JSON secret: ```bash echo '{"host":"db.example.com","port":5432,"username":"app","password":"secret"}' | \ wrangler secrets-store secret create \ --name DB_CONFIG --scopes workers --remote ``` ## Integration ### Service Bindings Auth Worker signs JWT with Secrets Store; API Worker verifies via service binding. See: [workers](../workers/) for service binding patterns. See: [api.md](./api.md), [gotchas.md](./gotchas.md)