Centrifugo real-time messaging relay for WebSocket pub/sub.


Overview

Centrifugo is a production-grade Go-based real-time messaging server running at relay.spacemusic.tv. It provides WebSocket pub/sub with channels, presence tracking, and message history.

Version: v6.6.2 (centrifugo/centrifugo:v6)

Why Centrifugo: The centrifuge-csharp SDK works identically in Avalonia desktop and Avalonia WASM (browser), enabling one C# codebase for all client platforms. See the relay service's research doc for the full evaluation of alternatives (Node-RED, custom Node.js, custom Go, MQTT).

Endpoints

Endpoint URL
WebSocket wss://relay.spacemusic.tv/connection/websocket
HTTP Publish API https://relay.spacemusic.tv/api/publish
HTTP Broadcast API https://relay.spacemusic.tv/api/broadcast
HTTP Batch API https://relay.spacemusic.tv/api/batch
Admin UI https://relay.spacemusic.tv/ (password-protected)
Prometheus Metrics https://relay.spacemusic.tv/metrics

The Admin UI is served on the same port as all other endpoints (admin.external: true in config), since Traefik only routes to one port per service.

Channel Namespaces

Centrifugo organizes channels into namespaces with different capabilities:

Namespace Auth Presence History Client Publish Anonymous
session:{id} JWT required Yes (join/leave events) 100 msgs / 300s TTL Subscribers can publish No
public:{id} None Yes No Anyone can publish Yes
system:{topic} JWT required Yes 50 msgs / 600s TTL No (server-push only) No

session:{id} -- For live performance sessions. Authenticated clients join with a JWT, can see who's present, and publish messages. History is preserved for late joiners (force_recovery enabled).

public:{id} -- For public status feeds. No authentication required, anonymous connections allowed. Good for public-facing status displays.

system:{topic} -- For system announcements and alerts. Only the server can publish (via HTTP API). Clients need a JWT to subscribe. History is preserved for 10 minutes.

Authentication

Centrifugo uses JWT tokens with HMAC HS256 for client authentication:

  • Connection token -- Required to establish a WebSocket connection (for session: and system: namespaces). Contains sub (user ID) claim.
  • Subscription token -- Required for private channel subscriptions. Must include both sub and channel claims.

Tokens can be generated via the API gateway at api.spacemusic.tv/api/relay/tokens.

The HTTP API uses an API key via the Authorization header for server-to-server communication.

Allowed Origins

WebSocket connections are restricted to:

  • relay.spacemusic.tv
  • stream.spacemusic.tv
  • connect.spacemusic.tv
  • dashboard.spacemusic.tv

HTTP API

The server-side HTTP API enables publishing from any backend service:

# Publish a message to a channel
curl -X POST https://relay.spacemusic.tv/api/publish \
  -H "Authorization: apikey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channel": "session:room1", "data": {"type": "note", "value": 60}}'

# Broadcast to multiple channels
curl -X POST https://relay.spacemusic.tv/api/broadcast \
  -H "Authorization: apikey YOUR_API_KEY" \
  -d '{"channels": ["public:status", "public:display"], "data": {"status": "live"}}'

All HTTP API endpoints are also available through the API gateway at api.spacemusic.tv/api/relay/*.

Deployment

Centrifugo runs via Docker Compose on the devpush_default network. Deployed via GitHub Actions SSH to /opt/spacemusic/relay/spacemusic-relay/.

Environment variables use the CENTRIFUGO_ prefix with underscored JSON path:

Variable Purpose
CENTRIFUGO_CLIENT_TOKEN_HMAC_SECRET_KEY JWT verification secret
CENTRIFUGO_API_KEY Server-side HTTP API key
CENTRIFUGO_ADMIN_PASSWORD Admin UI login
CENTRIFUGO_ADMIN_SECRET Admin UI session secret
Gotchas
  • Environment variable naming follows the JSON config path with underscores: CENTRIFUGO_CLIENT_TOKEN_HMAC_SECRET_KEY maps to client.token.hmac_secret_key in config.json
  • Do not use env_file in docker-compose -- it injects raw variables into the container causing "unknown var" warnings. Docker Compose reads .env for substitutions automatically.
  • admin.external: true is required so the admin UI serves on the main port (Traefik only routes to one port)

Monitoring

Prometheus scrapes Centrifugo at spacemusic-centrifugo:8000/metrics. These metrics power the "Relay" Grafana dashboard showing WebSocket connections, messages per second, and channel activity.

Performance

Tested benchmarks (2026-03-03):

  • Server-side batch of 1,000 messages: 4ms (~250k msg/s equivalent)
  • 15 concurrent WebSocket clients, 5 minutes: all operations sub-millisecond (p99 < 300us)
  • Memory at idle: 36 MB