Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Shyamalp16/CloudGaming/llms.txt

Use this file to discover all available pages before exploring further.

Signaling Server

The CloudGaming signaling server is a Node.js application that facilitates WebRTC peer connection establishment between hosts and clients.

Architecture Overview

The signaling server provides:
  • WebSocket Signaling: SDP offer/answer exchange and ICE candidate relay
  • Redis Pub/Sub: Multi-node horizontal scaling
  • Room Management: Isolated signaling channels per game session
  • Health Checks: Railway-compatible health/readiness endpoints
  • Rate Limiting: Redis-backed rate limiting for abuse prevention

Implementation

Server Initialization

// From Server/ScalableSignalingServer.js:478-511
async function main() {
    // Connect to Redis for pub/sub and state management
    await redisClient.connect();
    await subscriber.connect();
    log('info', 'Connected to Redis');
    
    // Subscribe to room pattern for cross-node message forwarding
    await subscriber.pSubscribe('room:*', handleRedisMessage);
    log('info', 'Subscribed to Redis channel pattern', { pattern: 'room:*' });
    
    // Start combined HTTP + WebSocket server
    const listenPort = process.env.PORT || config.wsPort;
    httpServer.listen(listenPort, () => {
        log('info', 'Scalable Signaling Server listening', { port: listenPort });
    });
}

WebSocket Signaling Protocol

Connection Flow

  1. Client connects with room ID:
    ws://signaling-server:3002?roomId=GAME-ABC-123
    
  2. Server validates and joins room:
    // From Server/ScalableSignalingServer.js:177-336
    async function handleNewConnection(ws, request) {
        const parameters = new url.URL(request.url, `ws://${request.headers.host}`).searchParams;
        const roomId = parameters.get('roomId');
        
        // Validate room ID
        if (!validateRoomId(roomId)) {
            ws.close(1008, 'Invalid roomId');
            return;
        }
        
        // Check rate limits
        const allowed = await rateLimiter.allow({
            namespace: 'conn',
            id: ip,
            limit: config.rateLimitConnPer10s,
            periodSeconds: 10
        });
        
        // Atomically join room via Redis
        const result = await atomicJoin(redisClient, roomKey, clientId, config.roomCapacity);
        if (result === -1) {
            ws.close(1000, 'Room is full');
            return;
        }
        
        // Add to local room map and start heartbeat
        localRooms.get(roomId).add(ws);
        startHeartbeat(ws);
    }
    
  3. Exchange signaling messages:
    // Client -> Server
    {
      "type": "offer",
      "sdp": "v=0\r\no=..."
    }
    
    // Server -> Peer
    {
      "type": "offer",
      "sdp": "v=0\r\no=..."
    }
    
    // Peer -> Server
    {
      "type": "answer",
      "sdp": "v=0\r\no=..."
    }
    
    // Server -> Client
    {
      "type": "answer",
      "sdp": "v=0\r\no=..."
    }
    

Message Types

Offer: Initial connection proposal from client
{
  "type": "offer",
  "sdp": "v=0\r\n..."
}
Answer: Host response to client offer
{
  "type": "answer",
  "sdp": "v=0\r\n..."
}
Candidate: Network connectivity information
{
  "type": "candidate",
  "candidate": "candidate:1 1 UDP 2130706431 192.168.1.100 54321 typ host",
  "sdpMid": "0",
  "sdpMLineIndex": 0
}
Peer Disconnected: Notification when peer leaves
{
  "type": "peer-disconnected"
}
Schema Error: Validation failure notification
{
  "type": "control",
  "action": "schema-error"
}

Schema Validation

// From Server/validation.js (referenced in ScalableSignalingServer.js:385-392)
const validation = validateSignalingMessage(parsedMessage);
if (!validation.ok) {
    log('warn', 'Dropping invalid signaling message');
    ws.send(JSON.stringify({ type: 'control', action: 'schema-error' }));
    incSchemaRejects();
    return;
}
Ensures all messages conform to expected structure before forwarding.

Configuration

Environment Variables

# Server
PORT=3002                              # HTTP + WebSocket port
NODE_ENV=production                    # Environment mode

# Redis
REDIS_URL=redis://localhost:6379      # Redis connection URL

# Security
REQUIRE_WSS=true                       # Force WSS in production
ALLOWED_ORIGINS=https://example.com    # CORS origins (comma-separated)
SUBPROTOCOL=cloudgaming-v1            # WebSocket subprotocol

# Rate Limiting
RATE_LIMIT_CONN_PER_10S=10            # Connection attempts per IP
RATE_LIMIT_IP_MSGS_PER_10S=500        # Messages per IP
RATE_LIMIT_ROOM_MSGS_PER_10S=1000     # Messages per room

# Capacity
ROOM_CAPACITY=10                       # Max clients per room
ROOM_TTL_SECONDS=3600                  # Redis key expiration
MESSAGE_MAX_BYTES=65536               # Max message size
BACKPRESSURE_THRESHOLD_BYTES=1048576  # Backpressure limit

# Monitoring
HEARTBEAT_INTERVAL_MS=30000           # Ping interval
DRAIN_TIMEOUT_MS=5000                 # Graceful shutdown timeout

Authentication (Optional)

# JWT Authentication
ENABLE_AUTH=true
JWT_SECRET=your-secret-key
JWT_ISSUER=cloudgaming-issuer
JWT_AUDIENCE=cloudgaming-audience
JWT_ALG=HS256
JWT_ROOMS_CLAIM=rooms                 # JWT claim containing allowed rooms

# Or JWKS URL for validation
JWT_JWKS_URL=https://auth.example.com/.well-known/jwks.json

Key Source Files

ScalableSignalingServer.js

Main signaling server implementation with WebSocket and Redis integration.

config.js

Configuration loader with environment variable parsing and defaults.

validation.js

Message schema validation using Zod for type safety.

rateLimiter.js

Redis-backed token bucket rate limiter implementation.
Deployment Tips:
  • Use Redis Cluster for high availability
  • Deploy multiple server instances behind a load balancer
  • Monitor /metrics endpoint with Prometheus
  • Set REQUIRE_WSS=true in production

Build docs developers (and LLMs) love