centrifugo

Installation
SKILL.md

Centrifugo Real-Time Messaging Integration

Centrifugo is a self-hosted, language-agnostic real-time messaging server. It handles persistent connections (WebSocket, SSE, HTTP-streaming, WebTransport, GRPC) and broadcasts messages via a channel-based PUB/SUB model. The application backend publishes to channels via Server API; Centrifugo delivers to online subscribers instantly.

Core Architecture

Backend App ──(HTTP/GRPC Server API)──> Centrifugo Cluster ──(WebSocket/SSE)──> Clients
                                        Redis/NATS Broker
                                        (for multi-node)

Key principle: All user-generated data flows through the application backend first (validate, persist, then publish to Centrifugo). Centrifugo is the transport layer, not the source of truth.

Procedures

Step 1: Determine Integration Pattern

Identify the real-time feature being built and select the appropriate pattern:

  1. Unidirectional publish (most common): Backend publishes to channels after processing user requests. Clients subscribe and receive updates passively.
  2. Bidirectional with proxy: Clients send data over the real-time connection; Centrifugo proxies events (connect, subscribe, publish, RPC) to the backend for validation.
  3. Server-side subscriptions: Backend controls which channels a user subscribes to (useful for personalized feeds, notifications).

Step 2: Configure Centrifugo Server

Generate a minimal configuration:

centrifugo genconfig  # Creates config.json

Or use Docker:

docker run -p 8000:8000 centrifugo/centrifugo:v6 centrifugo

Essential configuration structure (JSON, YAML, or TOML supported):

{
  "client": {
    "token": {
      "hmac_secret_key": "<SECRET>"
    },
    "allowed_origins": ["http://localhost:3000"]
  },
  "http_api": {
    "key": "<API_KEY>"
  },
  "channel": {
    "without_namespace": {
      "allow_subscribe_for_client": true
    }
  }
}

Read references/configuration.md for the full configuration reference including TLS, admin UI, environment variables, and advanced options.

Step 3: Set Up Authentication

Centrifugo authenticates clients via JWT or connect proxy.

JWT approach (recommended for most cases):

  • Backend generates a JWT with sub (user ID) and exp claims, signed with the configured HMAC/RSA/ECDSA key.
  • Client passes the token when connecting.
  • Supports token refresh for long-lived connections.
# Backend: generate connection JWT (Python example)
import jwt, time
token = jwt.encode(
    {"sub": "user123", "exp": int(time.time()) + 3600},
    "<HMAC_SECRET>", algorithm="HS256"
)
// Client: connect with token
const client = new Centrifuge("ws://localhost:8000/connection/websocket", {
  token: "<JWT_TOKEN>",
});
client.connect();

Connect proxy approach (alternative):

  • Centrifugo forwards connection requests to the backend endpoint for authentication.
  • No JWT needed; backend responds with user identity.

Read references/authentication.md for JWT claims, token refresh, proxy auth, and channel-level authorization tokens.

Step 4: Design Channel Structure

Channels are ephemeral strings that serve as message pathways. Use namespaces to apply different behaviors.

Naming conventions:

  • chat:room-123 — namespace chat, channel for room 123
  • notifications:user#42 — user-limited channel (only user 42 can subscribe)
  • $private:secret — private channel prefix (requires subscription token)

Namespace configuration:

{
  "channel": {
    "namespaces": [
      {
        "name": "chat",
        "presence": true,
        "history_size": 50,
        "history_ttl": "300s",
        "force_recovery": true,
        "join_leave": true
      },
      {
        "name": "notifications",
        "allow_user_limited_channels": true
      }
    ]
  }
}

Read references/channels.md for all channel options, namespace rules, and special channel prefixes.

Step 5: Publish from Backend (Server API)

Use HTTP or GRPC to publish messages from the application backend.

HTTP API — POST to /api/<method> with X-API-Key header:

# Publish to a channel
curl -X POST -H "X-API-Key: <KEY>" -H "Content-Type: application/json" \
  -d '{"channel": "chat:room-1", "data": {"text": "hello"}}' \
  http://localhost:8000/api/publish

# Broadcast to multiple channels
curl -X POST -H "X-API-Key: <KEY>" -H "Content-Type: application/json" \
  -d '{"channels": ["user:1", "user:2"], "data": {"text": "hello"}}' \
  http://localhost:8000/api/broadcast

Available API methods: publish, broadcast, subscribe, unsubscribe, disconnect, refresh, presence, presence_stats, history, history_remove, channels, info, batch.

Read references/server-api.md for all method signatures, request/response schemas, and HTTP API library links.

Step 6: Connect Clients with SDK

Official SDKs: centrifuge-js (browser/Node/React Native), centrifuge-go, centrifuge-dart (Flutter), centrifuge-swift (iOS), centrifuge-java (Android), centrifuge-python.

JavaScript client pattern:

import { Centrifuge } from "centrifuge";

const client = new Centrifuge("ws://localhost:8000/connection/websocket", {
  token: "<JWT>",
});

// Connection state handlers
client.on("connecting", ctx => console.log("connecting", ctx));
client.on("connected", ctx => console.log("connected", ctx));
client.on("disconnected", ctx => console.log("disconnected", ctx));

// Subscribe to channel
const sub = client.newSubscription("chat:room-1");
sub.on("publication", ctx => {
  console.log("received:", ctx.data);
});
sub.on("subscribing", ctx => console.log("subscribing", ctx));
sub.on("subscribed", ctx => console.log("subscribed", ctx));
sub.subscribe();

client.connect();

Client states: disconnected -> connecting -> connected (auto-reconnect with exponential backoff).

Subscription states: unsubscribed -> subscribing -> subscribed (auto-resubscribe on reconnect).

Read references/client-sdk.md for all SDK patterns, token refresh, presence/history from client, RPC calls, and Protobuf mode.

Step 7: Configure Event Proxy (Optional)

Proxy client events to the backend for validation and custom logic.

Supported proxy events:

  • connect — authenticate connections without JWT
  • refresh — extend client sessions
  • subscribe — validate channel access
  • publish — validate publications from clients
  • rpc — handle custom client-to-server calls
  • sub_refresh — extend subscription sessions
{
  "client": {
    "proxy": {
      "connect": {
        "enabled": true,
        "endpoint": "http://backend:3000/centrifugo/connect"
      },
      "rpc": {
        "enabled": true,
        "endpoint": "http://backend:3000/centrifugo/rpc"
      }
    }
  },
  "channel": {
    "namespaces": [
      {
        "name": "chat",
        "proxy": {
          "subscribe": {
            "enabled": true,
            "endpoint": "http://backend:3000/centrifugo/subscribe"
          },
          "publish": {
            "enabled": true,
            "endpoint": "http://backend:3000/centrifugo/publish"
          }
        }
      }
    ]
  }
}

Read references/proxy.md for proxy request/response schemas, GRPC proxy, header forwarding, and timeout configuration.

Step 8: Scale Horizontally (Production)

Memory engine (default): Single node only. Fast, no dependencies. Suitable for development and small deployments.

Redis engine: Multi-node clusters. Messages published on any node reach all subscribers.

{
  "engine": {
    "type": "redis",
    "redis": {
      "address": "redis://localhost:6379"
    }
  }
}

Supports Redis Sentinel, Redis Cluster, consistent sharding across multiple Redis instances, and Redis-compatible storages (Valkey, DragonflyDB, KeyDB, AWS ElastiCache).

NATS broker: Alternative for PUB/SUB only (no history/presence persistence).

Read references/engines.md for Redis Sentinel/Cluster setup, sharding, NATS configuration, and separate broker/presence manager configuration.

Step 9: Enable History and Recovery

History allows caching recent publications per channel. Recovery automatically restores missed messages after reconnection.

{
  "channel": {
    "namespaces": [
      {
        "name": "chat",
        "history_size": 100,
        "history_ttl": "600s",
        "force_recovery": true,
        "force_positioning": true
      }
    ]
  }
}
  • history_size + history_ttl: Define retention window.
  • force_recovery: Clients automatically recover missed messages on reconnect.
  • force_positioning: Detect publication gaps.
  • Cache recovery mode: Optimized for channels where only the latest state matters. Read references/channels.md for details.

Step 10: Add Observability

Centrifugo exposes Prometheus metrics at /metrics and supports structured logging.

{
  "log_level": "info",
  "prometheus": {
    "enabled": true
  },
  "health": {
    "enabled": true
  }
}

Metrics include: connections, subscriptions, publications, API calls, transport stats, and engine stats.

Anti-Patterns

Do NOT use Centrifugo history as a primary database

History is an ephemeral hot cache designed to reduce database load during reconnect storms. Always maintain a primary application database.

Do NOT rely solely on Centrifugo for data delivery

Design with graceful degradation. If Centrifugo goes down, the application should still function (just without real-time updates). Return data in API responses, not only via channels.

Do NOT skip allowed_origins in production

Empty allowed_origins blocks all browser connections. Configure it with the exact origins of the frontend application.

Do NOT publish directly from clients without proxy validation

Always validate client publications through either:

  • The application backend (clients call backend API, backend publishes to Centrifugo), or
  • Publish proxy (Centrifugo forwards client publish to backend for validation)

Do NOT use channels without namespaces in production

Namespaces provide granular control over channel behavior and permissions. Define a namespace for each real-time feature.

Do NOT ignore token expiration

Set reasonable exp claims in JWTs. Too short = excessive refresh requests. Too long = delayed user deactivation. Implement the token refresh callback in client SDKs.

Error Handling

  • If the client receives 102: unknown channel — verify the namespace is defined in configuration.
  • If the client receives 103: permission denied — check channel permissions (allow_subscribe_for_client, subscription JWT, or subscribe proxy).
  • If publications are not delivered — verify the channel name matches exactly (namespace prefix included). Ensure at least one subscriber exists.
  • If reconnection loops occur — check allowed_origins, JWT validity/expiration, and network connectivity.
  • If Redis engine fails to connect — verify address format (redis://, rediss:// for TLS, redis+sentinel:// for Sentinel, redis+cluster:// for Cluster).

Related Integrations

Centrifugo integrates well with any backend language/framework. Common setups include:

  • Node.js/Hono/Express: Use HTTP API or centrifuge npm package for server-side operations.
  • Go: Use the centrifuge Go library directly or the Centrifugo HTTP/GRPC API.
  • Python/Django/Flask: Use pycent library or direct HTTP API calls.
  • PHP/Laravel: Use phpcent library.

For the JavaScript client SDK (centrifuge-js), install via npm:

npm install centrifuge
# or
bun add centrifuge
Weekly Installs
37
GitHub Stars
301
First Seen
Mar 18, 2026
Installed on
opencode37
gemini-cli37
github-copilot37
codex37
amp37
cline37