Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/realtime-sfu/configuration.md
1# Configuration & Deployment23## Dashboard Setup451. Navigate to https://dash.cloudflare.com/?to=/:account/calls62. Click "Create Application" (or use existing app)73. Copy `CALLS_APP_ID` from dashboard84. Generate and copy `CALLS_APP_SECRET` (treat as sensitive credential)95. Use credentials in Wrangler config or environment variables below1011## Dependencies1213**Backend (Workers):** Built-in fetch API, no additional packages required1415**Client (PartyTracks):**16```bash17npm install partytracks @cloudflare/calls18```1920**Client (React + PartyTracks):**21```bash22npm install partytracks @cloudflare/calls observable-hooks23# Observable hooks: useObservableAsValue, useValueAsObservable24```2526**Client (Raw API):** Native browser WebRTC API only2728## Wrangler Setup2930```jsonc31{32"name": "my-calls-app",33"main": "src/index.ts",34"compatibility_date": "2025-01-01", // Use current date for new projects35"vars": {36"CALLS_APP_ID": "your-app-id",37"MAX_WEBCAM_BITRATE": "1200000",38"MAX_WEBCAM_FRAMERATE": "24",39"MAX_WEBCAM_QUALITY_LEVEL": "1080"40},41// Set secret: wrangler secret put CALLS_APP_SECRET42"durable_objects": {43"bindings": [44{45"name": "ROOM",46"class_name": "Room"47}48]49}50}51```5253## Deploy5455```bash56wrangler login57wrangler secret put CALLS_APP_SECRET58wrangler deploy59```6061## Environment Variables6263**Required:**64- `CALLS_APP_ID`: From dashboard65- `CALLS_APP_SECRET`: From dashboard (secret)6667**Optional:**68- `MAX_WEBCAM_BITRATE` (default: 1200000)69- `MAX_WEBCAM_FRAMERATE` (default: 24)70- `MAX_WEBCAM_QUALITY_LEVEL` (default: 1080)71- `TURN_SERVICE_ID`: TURN service72- `TURN_SERVICE_TOKEN`: TURN auth (secret)7374## TURN Configuration7576```javascript77const pc = new RTCPeerConnection({78iceServers: [79{ urls: 'stun:stun.cloudflare.com:3478' },80{81urls: [82'turn:turn.cloudflare.com:3478?transport=udp',83'turn:turn.cloudflare.com:3478?transport=tcp',84'turns:turn.cloudflare.com:5349?transport=tcp'85],86username: turnUsername,87credential: turnCredential88}89],90bundlePolicy: 'max-bundle', // Recommended: reduces overhead91iceTransportPolicy: 'all' // Use 'relay' to force TURN (testing only)92});93```9495**Ports:** 3478 (UDP/TCP), 53 (UDP), 80 (TCP), 443 (TLS), 5349 (TLS)9697**When to use TURN:** Required for restrictive corporate firewalls/networks that block UDP. ~5-10% of connections fallback to TURN. STUN works for most users.9899**ICE candidate filtering:** Cloudflare handles candidate filtering automatically. No need to manually filter candidates.100101## Durable Object Boilerplate102103Minimal presence system:104105```typescript106export class Room {107private sessions = new Map<string, {userId: string, tracks: string[]}>();108109async fetch(req: Request) {110const {pathname} = new URL(req.url);111const body = await req.json();112113if (pathname === '/join') {114this.sessions.set(body.sessionId, {userId: body.userId, tracks: []});115return Response.json({participants: this.sessions.size});116}117118if (pathname === '/publish') {119this.sessions.get(body.sessionId)?.tracks.push(...body.tracks);120// Broadcast to others via WebSocket (not shown)121return new Response('OK');122}123124return new Response('Not found', {status: 404});125}126}127```128129## Environment Validation130131Check credentials before first API call:132133```typescript134if (!env.CALLS_APP_ID || !env.CALLS_APP_SECRET) {135throw new Error('CALLS_APP_ID and CALLS_APP_SECRET required');136}137```138