A WebRTC application built with a Go signaling server and a SolidJS single-page app. Uses Redis to track connected peers and who is currently broadcasting.
- Go HTTP/WebSocket server (Gorilla WebSocket)
- Redis for lightweight presence state
- SolidJS + Vite frontend
- Optional TURN relay (coturn) for fallback when STUN/host fails
- Go 1.21+ (tested with recent Go releases)
- Bun for the frontend build
- Redis running and reachable (defaults to
localhost:6379)
# 1) Install frontend deps
cd frontend
npm install # or bun install
# 2) Build the frontend assets
npm run build # emits dist/ consumed by the Go server
# 3) Run Redis (if not already running)
redis-server &
# 4) Start the Go backend (serves the built frontend and /ws)
cd ../backend
go run main.goThen open http://localhost:8080 in multiple tabs to see peers join and start/stop broadcasting.
- Rooms are private and created on demand. Use the landing page “Create private room” button or
POST /api/roomsto get a{code, url}. - Share the room URL (e.g.,
/rooms/{code}) so peers can join and enter a display name. - WebSocket connections must include the room code (
/ws?room={code}); presence and broadcasts are isolated per room using Redis.
Environment variables (optional):
ADDR- HTTP listen address (default:8080)REDIS_ADDR- Redis address (defaultlocalhost:6379)STATIC_DIR- Path to the frontenddist/(default../frontend/dist)WS_PUBLIC_URL- Optional; explicit WebSocket URL to advertise to clients (defaults to request host/proto and/ws)STUN_URLS- Comma-separated STUN URLs (defaultstun:stun.l.google.com:19302)TURN_URLS- Comma-separated TURN URLs (e.g.,turn:TURN_HOST:3478?transport=udp,turn:TURN_HOST:3478?transport=tcp)TURN_USERNAME/TURN_PASSWORD- Credentials for TURN servers (if required)ICE_MODE- Optional;stun-turn(default) keeps both STUN+TURN,turn-onlydrops STUN and forces relay,stun-onlyskips TURN.
.env files are loaded from the project root, backend/.env, or ../.env.
Copy .env.example to .env and adjust TURN host/credentials to match your coturn config.
Debug ICE config at runtime with curl http://localhost:8080/debug/ice (shows servers and mode).
Client settings (WebSocket URL, ICE mode/servers) are available at GET /api/settings; the WS URL defaults to the incoming request host unless WS_PUBLIC_URL is set.
- Frontend:
npm run dev -- --hostfromfrontend/for hot reload; the app reads the signaling URL from/api/settings(setWS_PUBLIC_URLon the backend if the public host differs). - Backend:
go run main.gofrombackend/. The server resets Redis presence sets on startup to avoid stale peer lists after restarts.
- Coturn config lives in
coturn/. Copycoturn/turnserver.conf.exampletocoturn/turnserver.confand adjustrealm,external-ip, ports, and credentials. - Start coturn:
- Native:
turnserver -c coturn/turnserver.conf(stop via service manager orpkill turnserver). - Docker:
cd coturn && ./run-docker.sh(stop with./stop-docker.sh, container name defaults towebrtc-coturn).
- Native:
- Ensure TURN creds match
TURN_USERNAME/TURN_PASSWORDenvs. When TURN is configured, the backend passes both STUN and TURN entries to the frontend via the WebSocket welcome message; ICE will try host/STUN first and fall back to TURN. - Deployment runbook and pricing analysis live in
docs/TURN_DEPLOYMENT_GUIDE.mdanddocs/TURN_HOSTING_PRICE_ANALYSIS.md.