freertc
v0.1.33
Published
Cloudflare Worker signaling relay for WebRTC peers with D1 storage.
Maintainers
Readme
freertc Cloudflare Worker (WebSocket + D1)
This project provides a Cloudflare Worker signaling relay for WebRTC peers using the Peer Signaling Protocol (PSP) envelope shape.
Install from npm
Local project install:
npm install freertcGlobal install:
npm install -g freertcWhen you run the CLI from your project directory, freertc copies the required worker files into that directory on first run:
src/index.jspublic/index.htmlpublic/app.jsscripts/d1-schema.sqlwrangler.template.jsoncwrangler.workers-dev.jsonc
What this worker does
- Accepts WebSocket client connections at
/ws. - Validates PSP message envelopes (
psp_version,type,network,from,message_id,timestamp). - Supports discovery, negotiation, control, and extension message types.
- Stores peer announcements in Cloudflare D1 (
psp_announcements). - Stores directed signaling messages in Cloudflare D1 (
psp_relay). - Exposes a simple relay registry at
/api/v1/relayswhen D1 is configured. - Delivers queued relay messages when peers reconnect.
- Serves the browser demo from
public/.
Runtime scope
- The checked-in Cloudflare Worker runtime is
src/index.jswith Cloudflare D1 (DBbinding). - The Rust/WASM worker lives in
src/lib.rsand is optional; the default template now uses the JS worker path. - The built-in browser demo served by the Worker is
public/index.html+public/app.js. src/kv.jsanddemo/src/*are legacy/experimental code paths and are not used bywrangler devorwrangler deployin the current setup.
Supported message types
- Discovery:
announce,withdraw,discover,peer_list,redirect - Negotiation:
connect_request,connect_accept,connect_reject,offer,answer,ice_candidate,ice_end,renegotiate - Control:
ping,pong,bye,error,ack - Extension:
ext
Wrangler install wizard (recommended)
Use the interactive wizard from the project directory where you want the worker files and Wrangler config to live:
npx freertc wizardThe default command runs full setup mode (both):
npx freertcYou can also preselect full setup mode explicitly:
npx freertc setupGlobal install flow:
freertc wizard
freertcAfter install, freertc prints a quick-start reminder with the exact next command.
The wizard can:
- Copy the worker runtime files into your current project when they are missing.
- Verify Wrangler CLI.
- Create
wrangler.jsoncfromwrangler.template.jsoncif needed. - No domain? No problem. Press Enter at the domain prompt to deploy on a free
workers.devsubdomain. - Set Worker name automatically to
freertc-<your-domain>when a domain is provided. - Initialize local D1 schema for
wrangler dev. - Initialize remote D1 schema for deploy.
- Detect Rust build configs and install
worker-build/WASM target automatically when required. - Check existing Wrangler auth and only run
wrangler loginwhen needed. - Optionally run
npm run dev:cfandnpm run deploy.
Manual setup
1. Install dependencies
npm installIf you installed the npm package instead of cloning the repo, use npx freertc wizard instead of the repository scripts below.
2. Configure Wrangler
- If needed, copy
wrangler.template.jsonctowrangler.jsonc. - Set your Worker name, route(s), and D1 database values.
- Ensure
d1_databases[0].bindingisDB.
3. Initialize D1 schema
Local (for wrangler dev):
npm run d1:init:localRemote (for production):
npm run d1:init:remoteIf your database name is not freertc-signal, use Wrangler directly:
wrangler d1 execute <your-db-name> --local --file scripts/d1-schema.sql
wrangler d1 execute <your-db-name> --remote --file scripts/d1-schema.sqlLocal development
npm run devnpm run dev now runs the non-Cloudflare local runtime (plain Node.js + WebSocket).
Cloudflare/Wrangler runtime:
npm run dev:cfShortcut alias (same behavior):
npm run dev:localYou can also choose host/port:
HOST=127.0.0.1 PORT=8788 npm run dev:nodenpm run dev:cf checks local Rust Worker prerequisites automatically:
- Uses
wrangler.workers-dev.jsoncautomatically whenwrangler.jsoncis not present. - Only installs
worker-buildand the WebAssembly Rust target when the selected Wrangler config uses aworker-buildcommand. - The checked-in
wrangler.workers-dev.jsoncnow points tosrc/index.js, so standard demo runs do not require Rust/WASM setup.
Endpoints:
- WebSocket:
ws://127.0.0.1:8788/ws(npm run dev) - Health:
http://127.0.0.1:8788/health(npm run dev) - Demo UI:
http://127.0.0.1:8788/(npm run dev)
Cloudflare/Wrangler endpoints (default):
- WebSocket:
ws://127.0.0.1:8787/ws(npm run dev:cf) - Health:
http://127.0.0.1:8787/health(npm run dev:cf) - Demo UI:
http://127.0.0.1:8787/(npm run dev:cf) - Relay registry:
http://127.0.0.1:8787/api/v1/relays(GET/POST, when D1 is configured)
Deploy
npx freertc deployIf you installed freertc globally:
freertc deployRepository-only scripts:
npm run buildbuilds the Rust/WASM worker viaworker-build --release.npm run deploy:rawdeploys without--env production.npm run checkrunscargo check --target wasm32-unknown-unknownfor the Rust worker path.npm run devruns the standalone local relay (non-Cloudflare).npm run dev:cfruns Wrangler/Cloudflare local dev.npm run dev:noderuns a standalone local relay without Cloudflare/Wrangler.npm run dev:localis a no-env shortcut for the standalone local relay.
Troubleshooting custom domain deploys
If your custom domain returns:
{"error":"API key is missing"}that error is usually from Cloudflare routing/auth config, not from this Worker runtime.
Quick checks:
- Compare
https://<workers-subdomain>/healthandhttps://<custom-domain>/health. - Confirm your route/custom domain points to this Worker.
- Review Cloudflare Access/API Shield/WAF rules on the custom hostname.
- If deploying with
--env production, verify that environment is the one bound to the route.
Expected /health response includes JSON like:
{"ok":true,"version":"0.1.33","protocol_version":"1.0","peers":0}Auto WebRTC two-tab test
The demo defaults to Auto WebRTC and performs real offer/answer + ICE exchange over this Worker.
- Open
http://127.0.0.1:8787/in two tabs. - Set both tabs to the same
networkandsession_id. - Set opposite peer IDs using random hex strings:
- Tab A
from:fc2142e44ec5c76f1bd46ccbb1eb2ed48f66f64260a5299c871f37ac742fa0c9 - Tab B
from:3e45d44c4ce4f9304a53f42b978fd13d23f85df4d97a88f8eb33ec13a2f8f7b1
- Tab A
- Set each tab
toto the other tabfrom, or leavetoempty for auto-discovery. - Connect both sockets and click Start Auto Handshake in both tabs.
- When DataChannel is open, send chat messages.
Minimal client expectations
- Send
announcefirst to bind socket identity (from,network). - Include
session_idfor negotiation messages. - Include
tofor directed messages. - Use periodic
announceto refresh presence TTL and receive queued relay messages. - Use
pingfor liveness (pong) and keepalive.
Notes
- TTL is enforced using
timestamp + ttl_ms(default 30 seconds, max 120 seconds). - Malformed envelopes produce PSP
errorresponses.
