# Realtime WebSocket Broker web-app-starter includes an optional WebSocket-to-PHP broker under `server/realtime-broker/`. The broker is deliberately generic: it owns persistent WebSocket connections, while PHP decides app-specific behavior through a normal HTTP endpoint. ## Files - `server/realtime-broker/broker.js` - broker daemon and importable `RealtimeBroker` class - `server/realtime-broker/vendor/ws/` - vendored `ws` 8.14.2, MIT license - `server/realtime-broker/test-broker.js` - broker integration test - `lib/realtime.class.php` - PHP helper for backend-initiated commands - `views/api/realtime.php` - starter demo backend endpoint - `views/realtime-demo.php` - browser demo page - `js/u-wsconnection.js` - small browser connection helper ## Start locally ```bash cd /path/to/web-app-starter REALTIME_HOST=127.0.0.1 \ REALTIME_PORT=8009 \ REALTIME_BACKEND_URL=http://127.0.0.1/web-app-starter/?api/realtime \ REALTIME_COMMAND_TOKEN=change-me \ node server/realtime-broker/broker.js ``` Then open `/?realtime-demo` and connect. ## Browser URL In production, proxy `/live/` to the broker and let the browser connect to the same origin: ```nginx location /live/ { proxy_pass http://127.0.0.1:8009/live/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 3600s; } ``` If the broker is elsewhere, set `REALTIME_LIVE_URL` or `config/settings.php` `realtime/live_url`. ## PHP backend protocol For every browser connect/message/disconnect, the broker POSTs URL-encoded fields to `REALTIME_BACKEND_URL`: - `message`: JSON message - `connection`: JSON connection/session info Message types: - `session-connect` - `session-disconnect` - browser messages prefixed as `client-*` unless already prefixed The PHP endpoint returns a JSON array of commands. ```php 'session', 'data' => ['room' => 'demo']], ['type' => 'send', 'match' => ['room' => 'demo'], 'message' => ['type' => 'notice', 'text' => 'hello']], ])); ``` ## Backend commands ### `session` Merge data into connection session info. Without `match`, applies to the current connection. ```json {"type":"session","data":{"room":"demo","user_id":123}} ``` ### `send` Send a JSON message to the current connection or matching connections. ```json {"type":"send","match":{"room":"demo"},"message":{"type":"chat","text":"hello"}} ``` ### `close` Close the current or matching connections. ```json {"type":"close","match":{"user_id":123}} ``` ### `list` Return matching connection sessions to a command caller. ```json {"type":"list","match":{"room":"demo"}} ``` ### `log` Log a message if broker logging is enabled. ## PHP helper `Realtime` wraps command POSTs to the broker: ```php Realtime::broadcast(['type' => 'notice', 'text' => 'hello'], ['room' => 'demo']); Realtime::session(['room' => 'demo'], ['user_id' => 123]); Realtime::connections(['room' => 'demo']); Realtime::close(['user_id' => 123]); ``` Configure: ```php $GLOBALS['config']['realtime'] = [ 'broker_url' => 'http://127.0.0.1:8009/', 'command_token' => getenv('REALTIME_COMMAND_TOKEN') ?: '', ]; ``` ## Command security If `REALTIME_COMMAND_TOKEN` is set, command POSTs must include one of: - `token=` form field - `Authorization: Bearer ` - `X-Realtime-Token: ` If no token is set, broker commands are accepted only from `REALTIME_COMMAND_ALLOW` IPs. Use a token for new deployments. ## Broker status ```bash curl http://127.0.0.1:8009/status ``` Returns JSON counters including active connections, message counts, bytes, backend errors, and denied command attempts. ## systemd example `/etc/systemd/system/web-app-starter-realtime.service`: ```ini [Unit] Description=web-app-starter realtime broker After=network.target [Service] Type=simple WorkingDirectory=/var/www/web-app-starter Environment=REALTIME_HOST=127.0.0.1 Environment=REALTIME_PORT=8009 Environment=REALTIME_BACKEND_URL=http://127.0.0.1/web-app-starter/?api/realtime Environment=REALTIME_COMMAND_TOKEN=change-me ExecStart=/usr/bin/node server/realtime-broker/broker.js Restart=on-failure RestartSec=2 NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=true [Install] WantedBy=multi-user.target ``` ## Validation ```bash node --check server/realtime-broker/broker.js node --check server/realtime-broker/test-broker.js node server/realtime-broker/test-broker.js php -l lib/realtime.class.php php tests/smoke.php ``` ## Notes - The broker is optional and not started by the PHP app. - Keep app policy in PHP; keep the broker generic. - Do not put secrets in messages sent to clients. - Tune `REALTIME_MAX_MESSAGE_BYTES`, `REALTIME_MAX_BUFFERED_BYTES`, and backend timeout for larger apps.