How-to Guides
Huddles — audio, video, and screen share
WebRTC huddles with worker call sessions, captions, and optional recording.
Huddles combine the SDK createHuddle() helper with worker call sessions. Default media path is Cloudflare Realtime SFU (no rooms on the SFU — the Durable Object is the room). LiveKit JWT mint is a fallback when LIVEKIT_* is set.
Realtime SFU signaling
The Worker holds REALTIME_SFU_APP_ID and REALTIME_SFU_APP_SECRET. Browsers never see the app secret.
curl -X POST "$WORKER_URL/rooms/{roomId}/realtime/sessions" \
-H "Authorization: Bearer $MEMBER_JWT" \
-H "Content-Type: application/json" \
-d '{"sessionDescription":{"type":"offer","sdp":"v=0..."}}'Same sequence as Cloudflare's Echo HTML: create session with the offer, wait for ICE, POST .../tracks with local mid + trackName, then pull peers from GET /rooms/{roomId}/realtime/tracks. The app secret never goes to the browser.
GET /admin/calls/token with no provider returns that signaling map when Realtime SFU is configured, otherwise a LiveKit token or stub.
Dashboard: Huddles
Bill cap (not Cloudflare's meter)
Realtime includes 1 TB/month then $0.05/GB. The Worker cannot read Cloudflare's usage, so it keeps its own high estimate and refuses new huddles when that estimate hits the cap.
Defaults (override with Worker env):
REALTIME_SFU_MONTHLY_GB_CAP=80(stop at 80 GB estimated, not 1000)REALTIME_SFU_MAX_CONCURRENT=2REALTIME_SFU_MAX_SESSION_SECONDS=1200(20 minutes, then we settle the session)- Video off unless
REALTIME_SFU_ALLOW_VIDEO=true - Panic:
REALTIME_SFU_DISABLED=true
GET /rooms/{roomId}/realtime/budget returns the current estimate. Apply D1 migration 0225_huddle_sfu_budget.sql.
Create and start a call
curl -X POST "$WORKER_URL/admin/calls" \
-H "Authorization: Bearer $ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{
"roomId": "room_team",
"provider": "cloudflare-realtime",
"recordingEnabled": true,
"maxParticipants": 8
}'
curl -X POST "$WORKER_URL/admin/calls/{callId}/start" \
-H "Authorization: Bearer $ADMIN_JWT"
curl -X POST "$WORKER_URL/admin/calls/join" \
-H "Authorization: Bearer $ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{"callId":"...","displayName":"Alex","role":"host"}'Screen share and recording
Update participant state:
curl -X PATCH "$WORKER_URL/admin/calls/participants" \
-H "Authorization: Bearer $ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{"callId":"...","screenSharing":true}'Toggle server-side recording:
curl -X POST "$WORKER_URL/admin/calls/{callId}/recording" \
-H "Authorization: Bearer $ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'SDK — local huddle
import { createHuddle } from "@fluxy-chat/sdk";
const huddle = createHuddle({
roomId: "room_team",
audioEnabled: true,
videoEnabled: true,
screenShareEnabled: true,
captionsEnabled: true,
recordingConsent: false,
maxParticipants: 8,
});
await huddle.join();
await huddle.startScreenShare();
huddle.giveRecordingConsent();Stats
curl "$WORKER_URL/admin/calls/active" -H "Authorization: Bearer $ADMIN_JWT"
curl "$WORKER_URL/admin/calls/stats" -H "Authorization: Bearer $ADMIN_JWT"Optional E2E encryption can be layered via room E2E keys — see Voice huddles core doc.
HarnessAgent — External Agent Runtime Wrapper
`HarnessAgent` wraps external agent runtimes (Claude Code, Codex, custom coding agents) behind a unified interface compatible with AI SDK stream types.
Knowledge base connectors
Register URL and manual KB sources on the Worker, sync documents, and query RAG context for agents.