Overview
The matchmaker service manages the pool of available Windows hosts and assigns clients to hosts for streaming sessions. Key responsibilities:- Accept host registrations with heartbeat mechanism
- Track host availability (idle/busy/offline)
- Match clients to available hosts based on region and capacity
- Provide ICE server configuration (STUN/TURN) to clients
- Health monitoring and stale host cleanup
Architecture
Requirements
- Node.js 16+ and npm
- Redis 6+ (shared with signaling server)
- 512 MB RAM minimum
- HTTP hosting (Railway, Heroku, Vercel, etc.)
Installation
The matchmaker is located inServer/mm_server/:
Configuration
Environment Variables
The matchmaker uses the sameconfig.js as the signaling server. Configure these variables:
Configuration Reference
| Variable | Type | Description | Default |
|---|---|---|---|
PORT | number | HTTP API port | 8080 (use 3000 for local dev to match README examples) |
REDIS_URL | string | Redis connection URL | redis://127.0.0.1:6379 |
HOST_SECRET | string | Shared secret for host authentication | to-change-in-prod |
HOST_SECRET_PREVIOUS | string | Previous secret for rotation | - |
SIGNALING_PUBLIC_URL | string | Public WebSocket URL returned to clients | Required in production |
METERED_DOMAIN | string | Metered.ca domain for TURN servers | - |
METERED_API_KEY | string | Metered.ca API key | - |
TURN_EXPIRY_SECONDS | number | TURN credential lifetime | 14400 (4 hours) |
Running Locally
API Reference
POST /api/host/heartbeat
Hosts send heartbeats to register and maintain availability. Authentication: Bearer token withHOST_SECRET
Request:
| Field | Type | Required | Description |
|---|---|---|---|
hostId | string (UUID) | Yes | Unique host identifier |
roomId | string | Yes | WebRTC room ID for this host |
region | string | No | Geographic region (e.g., us-east, eu-west) |
status | enum | No | Host status: idle, busy, or allocated |
capacity | number | No | Max concurrent sessions (default: 1) |
availableSlots | number | No | Current available slots (default: capacity) |
Hosts must send heartbeats every 20-25 seconds. If no heartbeat is received for 30 seconds, the host is removed from the available pool.
POST /api/match/find
Clients request a host assignment for a streaming session. No authentication required (public endpoint) Request:- Sample up to 50 random hosts from Redis
- Filter by region (if specified) with 5x weight preference
- Select host with available capacity using weighted random
- Atomically decrement
availableSlotsusing Redis transactions - Return room ID and signaling configuration
GET /api/hosts
List all available hosts (for monitoring). Response:GET /api/hosts/ttl
Get TTL (time-to-live) for all hosts (debugging). Response:Health Endpoints
GET /- ReturnsokGET /health- Returns 200 withokGET /healthz- Returns 200 (for Railway/K8s)GET /readyz- Returns 200 (for Railway/K8s)
Deploying to Production
Railway Deployment
Link to the same Redis instance
In Railway dashboard:Both signaling and matchmaker should share the same Redis instance.
Deploy with custom start command
Railway settings → Deploy → Custom Start Command:Or add to
package.json:Docker Deployment
CreateDockerfile.matchmaker in Server/:
Serverless / Edge Deployment
TURN Server Configuration
For clients behind restrictive NATs, TURN servers are required for WebRTC connectivity.Using Metered.ca (Recommended)
Metered.ca provides managed TURN servers with automatic credential rotation:Sign up for Metered.ca
Visit metered.ca and create an account.
Get your credentials
From the dashboard, note:
- Domain: Your subdomain (e.g.,
yourcompany) - API Key: Secret key for generating credentials
- Generate short-lived TURN credentials when a client requests a match
- Return ICE servers including both STUN and TURN
- Credentials expire after 4 hours (configurable)
Self-Hosted TURN (coturn)
For self-hosting, use coturn:mm_server/Matchmaker.js to return your TURN server:
Monitoring and Debugging
Redis Inspection
Monitor Redis state:Logs
Key log messages:Testing Host Registration
Simulate a host:Server/mm_server/simulate_hosts.js for automated testing:
Troubleshooting
Error: Forbidden - Invalid host secret
Error: Forbidden - Invalid host secret
Cause:
HOST_SECRET mismatch between host and matchmakerSolution: Ensure both use the same secret:- Matchmaker
.env:HOST_SECRET=HELLO-MFS - Host
config.json:host.matchmaker.hostSecret: "HELLO-MFS"
Hosts timing out after 30 seconds
Hosts timing out after 30 seconds
Cause: Heartbeat interval too long or hosts not sending heartbeatsSolution:
- Verify host is running and connected
- Check host logs for heartbeat send confirmations
- Ensure
heartbeatIntervalMsin host config is under 25000 (25s) - Network issues may prevent heartbeats from reaching matchmaker
Match requests always return 'No hosts available'
Match requests always return 'No hosts available'
Diagnosis:Causes:
- No hosts have sent heartbeats
- All hosts are busy (
availableSlots: 0) - Hosts have expired (no heartbeat for 30+ seconds)
- Region mismatch (client requests
us-east, all hosts areeu-west)
TURN credentials not working
TURN credentials not working
Diagnosis: Check ICE servers returned by
/api/match/findSolutions:- Verify
METERED_DOMAINandMETERED_API_KEYare correct - Check Metered.ca dashboard for usage/errors
- Test TURN server directly with Trickle ICE
- Ensure TURN ports (typically 3478, 443) are not blocked
High Redis memory usage
High Redis memory usage
Cause: Stale host entries accumulatingSolution: The matchmaker automatically prunes stale hosts every 10 seconds. If memory still grows:
Security Best Practices
Next Steps
- Host Setup - Configure Windows hosts to register with this matchmaker
- Client Deployment - Configure client to use this matchmaker for host discovery
- Signaling Server - Ensure signaling server is configured and accessible