# Phlix Media Server — Deployment Configuration # SP7: WebSocket worker port binding documentation # # This file documents the deployment configuration for exposing the # WebSocket worker (SyncPlay) to the external network via HAProxy. # # Related files: # - deploy/haproxy.cfg : HAProxy configuration for WS port exposure # - config/server.php : WS worker settings — timeouts, JWT auth AND the # 'websocket.host'/'websocket.port' bind keys. Since R14-06 these keys # DO move the listening socket: src/Server/Runtime/ListenEndpoints.php # resolves the bind env (PHLIX_WS_HOST/PHLIX_WS_PORT) > config > shipped # default (websocket://0.0.0.0:8097), and start.php writes the resolved # pair back into the config pre-fork so the object always describes the # socket that accepts. # - start.php : WS worker initialization (~line 636, # ListenEndpoints::webSocket() — nothing hard-coded; move the bind by # env/config + restart, never by code edit) --- # Deployment metadata deployment: name: phlix-server version: "1.0" last_updated: "2026-10-14" # WebSocket worker configuration websocket: # Port the daemon's WS worker listens on: the RESOLVED bind, default # 8097 (env PHLIX_WS_HOST/PHLIX_WS_PORT > websocket.host/websocket.port # in config/server.php > shipped default; resolved by ListenEndpoints, # R14-06 — the value below mirrors the shipped default). internal_port: 8097 # External exposure via HAProxy. The shipped haproxy.cfg binds :8443 to # avoid colliding with the daemon's own :8097 listener (EADDRINUSE); # firewall the raw daemon port from untrusted networks: # sudo ufw deny 8097/tcp && sudo ufw allow 8443/tcp # To keep the client-visible port at 8097 instead, move the daemon's WS # bind to loopback via env or config — PHLIX_WS_HOST=127.0.0.1 in # /etc/phlix/env (or websocket.host in config/server.php) — restart the # daemon, then switch the frontend bind: since R14-06 this needs NO code # edit. See the PORT PREREQUISITE header in deploy/haproxy.cfg. exposed_port: 8443 exposed_interface: "0.0.0.0" # Health check health_check: # Port for external health monitoring (separate from WS traffic) port: 8098 # The WS worker answers a plain HTTP GET with 400 Bad Request — that IS # the alive signal (it speaks only the WebSocket handshake). 426 appears # only for a Sec-WebSocket-Version mismatch during a real handshake, so # no probe can elicit it; HAProxy therefore uses TCP connect checks. expected_status: 400 path: / # Connection settings connection: # Maximum idle time before WS connection is closed idle_timeout: 86400 # 24 hours in seconds # Maximum time for the TCP connect to the backend worker handshake_timeout: 5000 # milliseconds # Health check interval check_interval: 10 # seconds # Backend (internal WS worker) configuration backend: host: "127.0.0.1" port: 8097 # Health check settings (TCP connect check — never an HTTP status probe) check_fall: 2 # Mark down after 2 consecutive failures check_rise: 3 # Mark up after 3 consecutive successes # HAProxy configuration reference haproxy: config_file: "deploy/haproxy.cfg" user: "haproxy" group: "haproxy" max_connections: 4096 # Statistics endpoint (localhost only) stats: host: "127.0.0.1" port: 8404 path: "/stats" # Frontend configuration (see websocket.exposed_port above for the # prerequisite before moving this onto :8097) frontend: bind: "*:8443" mode: "http" # Backend configuration backend: server: "127.0.0.1:8097" mode: "http" # Port binding summary ports: http: internal: 8096 description: "HTTP/REST API worker (GET /health lives here)" websocket: internal: 8097 external: 8443 description: "WebSocket worker for SyncPlay realtime communication" websocket_health: port: 8098 description: "Health check frontend for WS worker (400 from a plain GET = alive; TCP checks inside HAProxy)" # The hub relay is an OUTBOUND-only client connection (src/Hub/RelayConsumer) # to the hub's relay listener (hub :8802). The server opens NO inbound # relay listener — there is no 9443 (or any) port to expose here. relay: listener: none direction: "outbound" description: "Relay tunnel to phlix-hub — outbound client connection only, no server-side port" # Verification commands verification: # Check WS worker liveness through the proxy (400 = worker alive; it only # speaks the WebSocket handshake) health_check: | curl -s -o /dev/null -w '%{http_code}\n' http://:8443/ # Check WS health frontend (400 = worker alive) health_endpoint: | curl -s -o /dev/null -w '%{http_code}\n' http://:8098/ # HTTP API health (different worker) http_health: | curl -s http://:8096/health # HAProxy stats stats: | curl -s http://localhost:8404/stats # Dependencies depends_on: - "SP6: WebSocket worker implementation (SyncPlayManager, WebSocketServer)" - "config/server.php: websocket timeouts + JWT handshake auth (SV-4.7)" - "start.php: WS worker bind resolved by src/Server/Runtime/ListenEndpoints.php (R14-06), default websocket://0.0.0.0:8097 — move via PHLIX_WS_HOST/PHLIX_WS_PORT or websocket.host/port + restart"