UNPKG

@durable-streams/client

Version:

TypeScript client for the Durable Streams protocol

246 lines (165 loc) 6.92 kB
--- name: go-to-production description: > Production readiness checklist for durable streams. Switch from dev server to Caddy binary, configure CDN caching with offset-based URLs, Cache-Control and ETag headers, Stream-Cursor for cache collision prevention, TTL and Stream-Expires-At for stream lifecycle, HTTPS requirement, request collapsing for fan-out, CORS configuration. Load before deploying durable streams to production. type: lifecycle library: durable-streams library_version: "0.2.1" requires: - server-deployment sources: - "durable-streams/durable-streams:PROTOCOL.md" - "durable-streams/durable-streams:packages/caddy-plugin/README.md" --- This skill builds on durable-streams/server-deployment. Read it first for server setup basics. # Durable Streams — Go to Production Checklist Run through each section before deploying to production. ## Server Checks ### Check: Using Caddy production server (not dev server) Expected: ```bash ./durable-streams-server run --config Caddyfile ``` Fail condition: Importing `DurableStreamTestServer` from `@durable-streams/server` in production code. Fix: Download the Caddy binary from GitHub releases and configure with a Caddyfile. ### Check: File-backed persistence configured Expected: ``` durable_streams { data_dir ./data max_file_handles 200 } ``` Fail condition: No `data_dir` in Caddyfile server uses in-memory storage and loses data on restart. Fix: Add `data_dir` pointing to a persistent directory. ## Transport Checks ### Check: HTTPS enabled Expected: ```typescript const res = await stream({ url: "https://your-server.com/v1/stream/my-stream", }) ``` Fail condition: Using `http://` URLs in production. Pre-signed URLs and auth tokens are bearer credentials HTTP exposes them in transit. HTTP/1.1 also limits browsers to ~6 concurrent connections per origin. Fix: Configure TLS on the Caddy server (Caddy provides automatic HTTPS by default). ## Stream Lifecycle Checks ### Check: TTL or expiration set on streams Expected: ```typescript const handle = await DurableStream.create({ url: "https://server.com/v1/stream/my-stream", contentType: "application/json", headers: { "Stream-TTL": "86400" }, // 24 hours }) ``` Fail condition: Streams created without TTL persist forever, causing unbounded storage growth. Fix: Set `Stream-TTL` (seconds) or `Stream-Expires-At` (ISO timestamp) on stream creation. Use exactly one, not both. ### Check: Not specifying both TTL and Expires-At Expected: ```typescript headers: { "Stream-TTL": "86400" } // OR headers: { "Stream-Expires-At": "2026-04-01T00:00:00Z" } ``` Fail condition: Providing both `Stream-TTL` and `Stream-Expires-At` returns 400 Bad Request. Fix: Use one or the other. TTL is relative (seconds from creation), Expires-At is absolute. ## CDN and Caching Checks ### Check: CDN-friendly URL structure Expected: Reads use offset-based URLs that are naturally cacheable: ``` GET /v1/stream/my-stream?offset=abc123 ``` The server returns `Cache-Control` and `ETag` headers automatically for historical reads. CDNs can cache and collapse requests 10,000 viewers at the same offset become one upstream request. Fail condition: Overriding or stripping `Cache-Control` headers at the CDN/proxy layer. Fix: Allow the server's `Cache-Control` and `ETag` headers to pass through to the CDN. ### Check: Stream-Cursor header preserved `Stream-Cursor` prevents CDN cache collisions when the same offset returns different data (e.g., after stream truncation). Ensure your CDN does not strip this header. Fail condition: CDN strips `Stream-Cursor` from responses. Fix: Configure CDN to pass through `Stream-Cursor` response header. ## Error Handling Checks ### Check: onError handler configured for live streams Expected: ```typescript const res = await stream({ url, offset: "-1", live: true, onError: (error) => { if (error.status === 401) return // Stop retrying return {} // Retry transient errors }, }) ``` Fail condition: No `onError` handler permanent errors (401, 403) silently retry forever. Fix: Add `onError` handler that stops retrying for non-transient errors. ## Common Production Mistakes ### CRITICAL Using HTTP in production with browser clients Wrong: ```typescript const res = await stream({ url: "http://api.example.com/v1/stream/my-stream" }) ``` Correct: ```typescript const res = await stream({ url: "https://api.example.com/v1/stream/my-stream" }) ``` Pre-signed URLs and auth tokens are bearer credentials. HTTP exposes these in transit. Also, HTTP/1.1 limits browsers to ~6 concurrent connections per origin. Source: packages/client/src/utils.ts warnIfUsingHttpInBrowser ### HIGH Not setting TTL or expiration on streams Wrong: ```typescript const handle = await DurableStream.create({ url: "https://server.com/v1/stream/my-stream", contentType: "application/json", }) ``` Correct: ```typescript const handle = await DurableStream.create({ url: "https://server.com/v1/stream/my-stream", contentType: "application/json", headers: { "Stream-TTL": "86400" }, }) ``` Without TTL, streams persist forever causing unbounded storage growth. Source: PROTOCOL.md TTL and Expiry section ### MEDIUM Specifying both TTL and Expires-At Wrong: ```typescript headers: { "Stream-TTL": "86400", "Stream-Expires-At": "2026-04-01T00:00:00Z", } ``` Correct: ```typescript headers: { "Stream-TTL": "86400", // OR Expires-At, not both } ``` The protocol requires exactly one. Providing both returns 400 Bad Request. Source: PROTOCOL.md TTL and Expiry section ### HIGH Tension: Ephemeral producers vs. persistent coordination This skill's patterns conflict with writing-data. `autoClaim: true` is convenient for serverless/ephemeral workers but sacrifices cross-restart coordination. Persistent long-running workers may benefit from explicit epoch management for proper multi-worker coordination. See also: durable-streams/writing-data/SKILL.md § Common Mistakes ## Pre-Deploy Summary - [ ] Using Caddy production server (not dev server) - [ ] `data_dir` configured for persistence - [ ] HTTPS enabled - [ ] TTL or Expires-At set on all streams - [ ] Not using both TTL and Expires-At together - [ ] CDN passes through Cache-Control, ETag, and Stream-Cursor headers - [ ] `onError` handler configured for live streams - [ ] `max_file_handles` tuned for expected stream count ## See also - [server-deployment](../server-deployment/SKILL.md) Initial server setup - [writing-data](../writing-data/SKILL.md) Producer configuration for production - [vercel-ai-sdk](../../../aisdk-transport/skills/vercel-ai-sdk/SKILL.md) Vercel AI SDK integration with resumable chat - [tanstack-ai](../../../tanstack-ai-transport/skills/tanstack-ai/SKILL.md) TanStack AI integration with multi-client sync ## Version Targets durable-streams v0.2.1.