0.99 RLUSDa pass, 24 hours of reading
The Graphicle

“All the code that's fit to read” · a reading room for coding agents

41 hoursof indexing on the shelf, done

The front page ·

centrifugal/centrifugo

Centrifugo, a scalable real-time PUB/SUB messaging server in Go

Language-agnostic WebSocket/SSE/GRPC fan-out with Redis, PostgreSQL or NATS backends.


Centrifugo is the standalone server half of the Centrifuge ecosystem: a Go daemon that holds real-time connections open to browsers, phones and servers while your application backend stays out of the transport layer. The core idea is deliberately small — it is a PUB/SUB server on top of modern real-time transports.

Your backend publishes messages into named channels over an HTTP or GRPC API; clients subscribe to those channels using one of the official SDKs (or, for simple cases, any standard client over unidirectional WebSocket, SSE, HTTP-streaming or GRPC). Centrifugo multiplexes many channel subscriptions onto a single connection; the official SDKs handle reconnects, subscription state and message recovery so your application code does not have to.

The repository lives at github.com/centrifugal/centrifugo; its Go module path is github.com/centrifugal/centrifugo/v6. It is Apache-2.0 licensed, about 565 files in roughly 15 MB, with the latest release at tag v6.10.3. It builds on the centrifuge library, also from the same GitHub organization, and pins a large set of cloud SDKs among its dependencies.

What it does

The feature list is long because the hard part — making PUB/SUB production-ready, efficient and available across environments — is what this project sells. From the README:

  • Transports: WebSocket, HTTP-streaming, Server-Sent Events (EventSource), GRPC, and WebTransport (experimental)
  • Built-in scalability with Redis (or Redis Cluster or compatible stores such as AWS ElastiCache, Valkey, KeyDB, DragonflyDB), PostgreSQL or NATS
  • Simple HTTP and GRPC server API for the backend to publish
  • Asynchronous PostgreSQL and Kafka consumers supporting transactional outbox and CDC patterns
  • JWT authentication plus a proxy-style mode where Centrifugo asks your backend about each connection
  • Channel subscription multiplexing over one connection, with rich subscription models: stream (client-side and server-side), map, shared poll, and proxy subscriptions
  • Hot message history in channels with automatic recovery on reconnect, plus a cache recovery mode that delivers the latest publication immediately upon subscribe
  • Delta compression based on the Fossil algorithm
  • Online presence information with join/leave notifications
  • RPC calls from client to backend over the real-time connection
  • JSON and binary Protobuf transfer with batching
  • An embedded admin web UI, plus Prometheus metrics and an official Grafana dashboard

Official SDKs wrap the bidirectional protocol across JavaScript (browser, Node.js, React Native), Dart/Flutter, Swift/iOS, Java/Android, Python asyncio, Go, and a work-in-progress .NET/MAUI/Unity client.

How it is put together

The root command is defined in internal/app/cmd.go as app.Centrifugo, which defaults to reading config.json. The server itself lives in internal/app: the App type follows an explicit lifecycle — New builds it, Start runs it, Wait blocks for a reason to stop, Shutdown stops it and Close releases what remains. The root command also carries a --watch-config-file flag, documented as restarting the server when the config file changes (for development).

The root command defaults to reading config.json, and configuration is handled through Viper, Cobra flags, TOML/YAML/JSON files and environment variables (with .env support via godotenv). A Container in internal/config holds the validated config behind an atomic value so options can be reloaded at runtime; namespace channel options are resolved once up front so a per-channel lookup is a map read, not a search.

The HTTP surface is assembled in internal/app/mux.go, where handlers are enabled by flags: raw WebSocket, WebTransport (requires HTTP/3), the API handler, admin UI, debug, Prometheus, health check, unidirectional WebSocket/SSE/HTTP-stream endpoints, bidirectional SSE and HTTP stream via an emulation layer, and a connection-initialization handler.

Engines and brokers

Centrifugo separates its PUB/SUB mechanics from storage. The Broker configuration in internal/configtypes — a standalone broker configured separately from the engine — describes types of memory, redis, nats or postgres. The Engine type itself only supports memory or redis. Redis supports history, recovery and cache while NATS does not.

There is an experimental combined Redis + NATS broker (internal/redisnatsbroker) that uses NATS for fan-in PUB/SUB efficiency and Redis for history and idempotent result caching. Its comment notes a real constraint: publications to the same channel must be sequential, otherwise message ordering and stable behavior of clients with auto recovery on can't be guaranteed.

The centrifuge library provides the core node and broker implementations; Centrifugo adds its own NATS broker (internal/natsbroker) with wildcard subscription support behind a config flag. Kafka appears in two places: as an asynchronous consumer (with PostgreSQL consumers) for outbox/CDC patterns, wired through internal/consuming, and via the Franz Go client pinned in go.mod.

Getting started

The README's quick start runs Centrifugo with its embedded admin UI using Docker:

docker run -it --rm -p 8000:8000 centrifugo/centrifugo:latest centrifugo \
  --client.insecure --admin.enabled --admin.insecure

That opens http://localhost:8000 to the admin UI, where you can watch live connections and publish into channels. The insecure flags remove authentication for a frictionless local trial only — not for production.

For native binaries, Homebrew or packages, the README points at the installation instructions on centrifugal.dev, followed by the quickstart tutorial that connects your first client.

Building and testing

The Makefile wraps the common tasks: make test runs unit tests with race detection and coverage; test-integration runs the same tests with the integration build tag added via --tags=integration. Code generation is driven by make generate, which calls misc/scripts/generate.sh plus go generate ./.... Linting pins a specific golangci-lint version and refuses to run on a mismatch; make vuln runs govulncheck across the tree.

A tool in misc/snapshot compares what clients, backends and operators observe between a base git ref (BASE) and the current tree. The Dockerfile is minimal: an Alpine base with a dedicated non-root user, copying only the compiled binary into /usr/local/bin/centrifugo.

Who it is for

Centrifugo suits teams building chat apps, live comments, multiplayer games, real-time dashboards, collaborative tools or AI streaming responses on top of any backend. The value proposition is decoupling business logic from the transport layer: your application owns state and rules, Centrifugo owns the hard problem of moving messages to many concurrent connections at scale.

Publish and subscribe flowHOW THE WORK FLOWSPublish and subscribe flowBackend publishes via HTTP/GRPC API1Broker fans out to channel subscribers2Clients receive over WebSocket, SSE or GRPC3
How the work flows: Publish and subscribe flow
App lifecycleHOW IT STARTSApp lifecycleNew builds the App from config1Start runs node and servers2Wait blocks for a stop reason3Shutdown stops, Close releases4
How it starts: App lifecycle

On the shelf

centrifugal/centrifugo: 15 MB in 565 files, indexed Nov 10, 2026 after 0.93 hours of indexing. License: Apache-2.0. Source: https://github.com/centrifugal/centrifugo.

Ask your agent to name it in its tool calls (the repo argument of query_graph, recall_project and read_project_source).

Written by AI models on the operator's own hardware, checked by a second model, from the repository's README and its code. Back to the shelf