Releases: centrifugal/centrifugo
Release list
v6.9.2
Centrifugo is an open-source scalable real-time messaging server. It instantly delivers messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE), GRPC, WebTransport). Centrifugo is built around channel subscriptions – so it's a user-facing PUB/SUB server.
Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, AI streaming responses, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.
Official client SDKs are available for JavaScript (browser, Node.js, React Native), Dart/Flutter, Swift, Java, Python, Go, and .NET. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.
For details, go to the Centrifugo documentation site. For runnable demos see centrifugal/examples.
What's changed
Improvements
- Connection runtime stability, consistency, and performance improvements coming from the underlying Centrifuge library. Under connection churn and concurrent subscribe/unsubscribe on the same channel, Centrifugo now keeps its internal subscription state consistent, fixes several resource and presence leaks, and avoids possible metric drift. The periodic presence updates also use noticeably less CPU and memory. As part of this work some internal operations became up to 500x faster under certain conditions (centrifugal/centrifuge#590).
- Less memory allocation and garbage collection pressure on the message broadcast path. This mostly helps nodes that deliver many messages per second (centrifugal/centrifuge#598).
- Kafka consumer: copy fetched records into a compact slice before putting them into the internal partition queue. Previously the queued records could keep the whole underlying fetch buffer in memory, so memory usage grew more than expected when a consumer was lagging behind (#1195).
- Redis: read commands now fail within the expected time when Redis is unreachable, instead of silently retrying until Redis comes back. This makes behavior during a Redis outage predictable and matches how writes already worked (#1191 and centrifugal/centrifuge#591).
- Redis: bound the worst-case time to detect a silently stalled connection (the peer stops replying but the TCP connection stays open) to under 5 seconds by tuning the keepalive (#1192 and centrifugal/centrifuge#592).
- Redis: verify PUB/SUB delivery with liveness probes to detect a broken subscription connection earlier (centrifugal/centrifuge#594).
- Add per-IP throttling of failed attempts on the admin password login endpoint (
POST /admin/auth) to slow down brute-force. A valid login is not affected, even while an attack is in progress. This is best-effort protection only – the admin endpoint should still be protected at the infrastructure level (firewall rules, private network, authenticating reverse proxy) (#1204). - Harden the binary (Protobuf) protocol frame decoder so that a crafted length prefix can no longer make the server allocate too much memory. The configured message size limit is now always applied when reading client commands (#1203 and centrifugal/centrifuge#612).
- Redis Sentinel: the Sentinel client now periodically refreshes its topology, so changes such as a new master after a failover are picked up more reliably (#1201 and centrifugal/centrifuge#611).
- Redis Sentinel: Centrifugo also inherited several important Redis Sentinel setup stability improvements from the updated
rueidisclient (v1.0.77 release notes).
Fixes
- Fix a possible server process crash (nil pointer panic) that could happen when a delta publication failed to encode to JSON. The broadcast goroutine could panic and terminate the whole node (centrifugal/centrifuge#597).
- Fix a case where a client could end up with several concurrent connections on the server under Redis load. On disconnect the transport is now closed before the per-channel cleanup, so a slow Redis no longer delays the socket teardown of the old connection (centrifugal/centrifuge#595).
- Async consumers: do not acknowledge messages that were still being consumed during shutdown, so such messages are re-delivered later instead of being lost (#1196).
- Logging: raise the internal log handler buffer from 64 to 1024 to reduce the chance of blocking on logging under bursts (#1190).
Miscellaneous
- This release is built with Go 1.26.6.
- Dependency updates.
- We detached App-owned state with stream subscriptions to its own blog post. It was part of PostgreSQL stream broker post before, but the mechanism is generic and not only PostgreSQL specific - so it's better to have it separate.
- Base Docker image updated to Alpine 3.24 (#1194).
- See also the corresponding Centrifugo PRO release.
v6.9.1
Centrifugo is an open-source scalable real-time messaging server. It instantly delivers messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE), GRPC, WebTransport). Centrifugo is built around channel subscriptions – so it's a user-facing PUB/SUB server.
Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, AI streaming responses, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.
Official client SDKs are available for JavaScript (browser, Node.js, React Native), Dart/Flutter, Swift, Java, Python, Go, and .NET. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.
For details, go to the Centrifugo documentation site. For runnable demos see centrifugal/examples.
What's changed
Improvements
- Possibility to choose color accents in admin web UI.
Fixes
- Admin web UI: add previously missing API commands to the web UI – map API (
map_publish,map_remove,map_read_state,map_read_stream,map_stats,map_clear), shared poll API, etc. – and update the embedded web UI (#1184, commit3fc933d9). - Fix a
WARNlog about a missingdevkey on start – some options and flags left over from the dev page removed in v6.9.0 were not fully cleaned up (#1185, commit8cee857f). - Admin: drop the undocumented and unused fallback that read the admin auth token from a
tokenURL query parameter – the token must be supplied via theAuthorizationheader (#1187, commitbc33546f). Addresses GHSA-v4h4-9j4c-ghgp - Removed an unused map
orderedoption (#1186, commit8752f242).
Miscellaneous
⚠️ Reminder: Centrifugo v6.9.0 had breaking changes in server configuration.- Docs: expanded the load balancing chapter with configs for Envoy, Caddy, and Traefik (in addition to Nginx and HAProxy) – all validated in a playground running two nodes behind each proxy across every transport (WebSocket, HTTP streaming, SSE) over plaintext and TLS, up to 10k connections.
- Docs: added interactive widgets throughout the documentation to clarify concepts where newcomers get confused most – for example the connection JWT explorer, subscription JWT explorer, channel resolver, subscribe permission model, and interactive proxy explorer.
- This release is built with Go 1.26.5
- Dependency updates
- See also the corresponding Centrifugo PRO release.
v6.9.0
This release comes with ⚠️ BREAKING CHANGES ⚠️
TLDR: Proxy http_headers is now transport-only; Need to configure explicit emulated_headers list for allowed emulated headers in Centrifugo server configuration.
Before v6.9.0, the proxy http_headers option forwarded headers from two different sources:
- transport-level headers — set on the real connection request (by the client transport or a reverse proxy/gateway in front of Centrifugo);
- client-supplied emulated headers — sent by the client inside its connect frame via the headers emulation feature.
In v6.9.0 these two sources are split into separate options:
| Option | Source | Trust |
|---|---|---|
http_headers |
the real connection request only | controllable by your edge (a proxy/gateway can set/strip it) |
emulated_headers |
the client's headers emulation map only | always client-controlled — always an untrusted input |
http_headers no longer forwards emulated headers. To forward a header supplied via headers emulation, you must now list it in the new emulated_headers option near http_headers.
Why we had to make this a breaking change. Before v6.9.0 a name listed in http_headers could be filled from either the real connection request or the client's emulation map. That made a trusted header like x-user-id forgeable in a way that is not the operator's mistake:
- An operator adds
x-user-idtohttp_headersfor a legitimate reason — their reverse proxy sets it, and they want that identity forwarded to the backend. They have no reason to expect this also lets a client supplyx-user-idvia emulation, especially if they have never heard of the headers emulation feature. - That reverse proxy might set
x-user-idonly for authenticated requests and leave it fully unset otherwise. - A client then sends
x-user-idin its connect frame via emulation. For any request where the proxy did not set the real header, Centrifugo forwarded the client's forged value to the backend, which trusted it as an authenticated identity.
A setup where the proxy always sets x-user-id (for example to an empty string even for anonymous requests) is not vulnerable. But Centrifugo cannot inspect your proxy to know whether you did that, and cannot tell a genuine x-user-id from a client-emulated one at request time — so there is no way for Centrifugo to make this safe automatically or to warn only the affected setups.
The only robust fix is structural to make operators aware of the feature: split the sources so http_headers is incapable of carrying a client-emulated value. A name in http_headers is now sourced only from the real HTTP connection; anything a client sends via emulation must be explicitly opted into emulated_headers (and is clearly documented as untrusted input). This removes the possibility of the flaw entirely, rather than relying on every operator to configure their proxy defensively against a feature they may not even know exists.
Are you affected? Only if you rely on headers emulation:
- using headers option in
centrifuge-js - using headers option in
centrifuge-dart(web platform only — on nativedart:iothese are sent as real WebSocket upgrade headers and are not affected) - using headers option in
centrifuge-csharp - using unidirectional transports and passing
headersas part of ConnectRequest
You are not affected if you are not using headers at all or only forward real transport headers (e.g. Cookie, Authorization, X-Real-Ip set by your reverse proxy or by native clients) — those keep working under http_headers with no change.
How to migrate: Before upgrading to v6.9.0, add the header names you allow to be sent over headers emulation to emulated_headers proxy configuration object option near http_headers. Apply this change to every proxy that used emulated headers (connect, refresh, channel proxies, RPC proxies, and any named proxies in the top-level proxies list).
{
"client": {
"proxy": {
"connect": {
"enabled": true,
"endpoint": "https://your_backend/centrifugo/connect",
"http_headers": ["X-User-Id", "Authorization"],
"emulated_headers": ["Authorization"]
}
}
}
}If you can't decide which headers to move right away, but need to update to v6.9.0 – then as a workaround, copy the whole http_headers list into emulated_headers nearby to preserve the pre-v6.9.0 behavior, then trim emulated_headers down to only the names your backend treats as untrusted client input.
Long-term, we will also rename headers to emulated_headers in SDKs that provide this feature.
Please reach out in the community rooms if the help with the migration is needed.
The security issue was reported in GHSA-9468-v6mj-fppw.
Improvements
- NATS JetStream consumer: recreate the consumer on
ErrConsumerDeleted,ErrConsumerNotFoundandErrStreamNotFoundinstead of getting permanently stuck (#1166, commit920d0c23). By @thuy-le-kafi. - Reworked web admin UI — now Vite-based, uses CodeMirror instead of Monaco (for offline work) and includes various UX improvements (#1169, commit
fa984f0f). - NATS JetStream consumer: back off redelivery with capped exponential delay instead of an instant
Nak, avoiding hot redelivery loops on a poison message (#1175, commitddda397e). - Google Pub/Sub consumer: re-subscribe with capped backoff on a receive error instead of exiting the receive loop (which previously left the consumer silently stopped) (#1176, commit
5f6b3871). - NATS JetStream consumer: reset the consume backoff only after a session has run healthy long enough, preventing rapid reconnect flapping (#1176, commit
c591b954).
Fixes
- Authentication: hold the read lock across the whole token verification to fix a data race between token verification and JWKS/config reload (#1168, commit
e30aca89). - PostgreSQL broker: fix
hashtext(channel)integer overflow that could panic shard routing when the hash landed exactly onINT_MIN(#1170, commitb52a3cb4). - PostgreSQL broker: self-heal a partial fresh install where only one of the JSONB/binary variant tables was created, which previously wedged schema setup (#1171, commit
cd85c450). - PostgreSQL broker: fix publication timestamps being zeroed by incorrect
timestamptztext parsing (#1174, commit3e163854). - PostgreSQL stream broker: fix reverse history pagination returning already-seen or empty pages and never reaching older messages (#1177, commit
2a58bf7f). - PostgreSQL outbox: ping the advisory-lock connection while the worker is busy (not only when idle) so a silently dropped lock session is detected promptly, preventing dual-leader processing (#1172, commit
aee4cb6c). - NATS: strip the internal epoch tag before delivering publications so it no longer leaks to clients (#1174, commit
05fb837c). - Azure Service Bus: abandon (instead of complete) a message when processing fails so it is redelivered rather than silently dropped (#1173, commit
0b59fc3a). - Azure Service Bus: guard a boolean application-property type assertion against a panic on non-string values (#1172, commit
4cce70ff). - Unidirectional WebSocket: clear the HTTP/2 write deadline after a frame ping — a leftover expired deadline could otherwise fail the stream permanently on HTTP/2 ([#1175](https://github.com/centrifu...
v6.8.4
Centrifugo is an open-source scalable real-time messaging server. It instantly delivers messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE), GRPC, WebTransport). Centrifugo is built around channel subscriptions – so it's a user-facing PUB/SUB server.
Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, AI streaming responses, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.
Official client SDKs are available for JavaScript (browser, Node.js, React Native), Dart/Flutter, Swift, Java, Python, Go, and .NET. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.
For details, go to the Centrifugo documentation site. For runnable demos see centrifugal/examples.
What's changed
Fixes
- Centrifugo now bounds the size of a WebSocket message after
permessage-deflatedecompression, see #1162.websocket.message_size_limitalone only bounded the compressed bytes received on the wire, so without an additional limit a small compressed frame could be decompressed into a much larger amount of memory (a "decompression bomb" attack). By default, the limit is derived frommessage_size_limitmultiplied by the default multiplier (10); messages exceeding it cause Centrifugo to close the connection with amessage too bigclose code.
Two new optionswebsocket.decompressed_message_size_limitanduni_websocket.decompressed_message_size_limitallow tuning this limit if needed. Only effective when compression is enabled. Reported by @alanturing881 via GHSA-q6mr-3g59-5m8x.
Miscellaneous
- New blog post Scaling Redis Pub/Sub to Millions of Channels and Hundreds of Subscriber Nodes which shares the experience of working with Redis Pub/Sub in Centrifugo.
- PostgreSQL broker metrics were split into per-kind
broker_*/map_broker_*subsystems and renamed with apostgres_prefix to align with the rest of the broker metric conventions, see #1161. Previously stream and map PG brokers shared apg_broker_*subsystem and were told apart by abrokerlabel, so brokers with the same (or default) name collided. This is a breaking change (but PostgreSQL is still experimental so we decided to change it) for dashboards and alerts in deployments using the PostgreSQL broker – thebrokerlabel is nowbroker_name, and metric names changed as follows:pg_broker_cleanup_rows_deleted_total→broker_postgres_cleanup_removed_total,pg_broker_outbox_cursor_lag_seconds→broker_postgres_outbox_cursor_lag_seconds/map_broker_postgres_outbox_cursor_lag_seconds,pg_broker_partitions→broker_postgres_partitions/map_broker_postgres_partitions. See exposed metrics for the full list. - This release is built with Go 1.26.4
- Dependency updates
- See also the corresponding Centrifugo PRO release.
v6.8.3
Centrifugo is an open-source scalable real-time messaging server. It instantly delivers messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE), GRPC, WebTransport). Centrifugo is built around channel subscriptions – so it's a user-facing PUB/SUB server.
Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, AI streaming responses, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.
Official client SDKs are available for JavaScript (browser, Node.js, React Native), Dart/Flutter, Swift, Java, Python, Go, and .NET. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.
For details, go to the Centrifugo documentation site. For runnable demos see centrifugal/examples.
What's changed
Improvements
- New
auto_cache_recoverchannel namespace option to automatically recover subscriptions on (re)subscribe without the subscriber requesting recovery itself, see #1158. In cache recovery mode this delivers the latest channel publication on every (re)subscribe – without the client providing an emptysinceposition itself. It also enables this for server-side subscriptions, which is especially useful for unidirectional clients that may not even know channel names. The option requiresforce_recoveryandforce_recovery_modeset tocache. Subscribe and connect proxies may also enable it per subscription with the newcache_recoverfield inSubscribeOptions. - OpenTelemetry: Centrifugo node ID is now used as the
service.instance.idresource attribute, see #1155. Each Centrifugo process now reports telemetry under a distinct identity, which avoids backends that require points of a time series to arrive in order (notably Google Cloud Managed Service for Prometheus) rejecting or collapsing metrics when several instances report under the same identity.OTEL_SERVICE_NAMEandOTEL_RESOURCE_ATTRIBUTESstill take precedence over Centrifugo defaults. - All official Centrifugo SDKs now support a
getStatesubscription callback – read the stream position first, then load your initial state, and return the position so the SDK subscribes from exactly there and recovers on every reconnect. This closes the gap between loading state from your own database and subscribing, see Using recovery in your app. - All official Centrifugo SDKs now support publication filtering by tags – clients can subscribe with a filter expression so only publications whose tags match are delivered, reducing bandwidth and client-side processing (previously available in
centrifuge-jsonly).
Documentation
- The Client protocol chapter was reworked and now ships with diagrams explaining the frame structure, command/reply sequence, push delivery, ping-pong, batching, and the JSON/Protobuf formats.
- The History and recovery chapter was significantly expanded with diagrams covering stream vs cache recovery modes, the recovery decision flow, and recovery storm mitigation.
- The GrandChat tutorial was actualized for 2026 – the Django/React code was modernized to recent versions and made more idiomatic and type-safe, and a new Two-column layout chapter shows how to build a Telegram/Slack-style messenger layout (room list and open room side by side) live from a single personal-channel subscription.
Miscellaneous
- The
linux/386(32-bit x86) binary is no longer published in releases, see #1154. - This release is built with Go 1.26.4
- Dependency updates
- See also the corresponding Centrifugo PRO release.
v6.8.2
Centrifugo is an open-source scalable real-time messaging server. It instantly delivers messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE), GRPC, WebTransport). Centrifugo is built around channel subscriptions – so it's a user-facing PUB/SUB server.
Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, AI streaming responses, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.
Official client SDKs are available for JavaScript (browser, Node.js, React Native), Dart/Flutter, Swift, Java, Python, Go, and .NET. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.
For details, go to the Centrifugo documentation site. For runnable demos see centrifugal/examples.
What's changed
Improvements
- OpenTelemetry: authenticate the OTLP exporter with Google Cloud Application Default Credentials (ADC) via the new
google_cloud_adc_authoption, see #1143 and #1148. This allows exporting traces directly to Google Cloud's OTLP endpoint (telemetry.googleapis.com) without a sidecar collector, and works with both thegrpcandhttp/protobufexporter protocols. - Kafka consumer: added a configurable
dial_timeout(default3s) for establishing a TCP connection to a single broker, and made the initialPingtimeout scale with the number of seed brokers so discovery no longer fails prematurely when some brokers are unreachable, see #1151. - Centrifugo official Helm chart now supports k8s Gateway API - see Helm chart 13.3.0 release
- Centrifugo now does not embed generated JSON config schema - configuration structure for
configdocis calculated in runtime, #1153
Fixes
- Kafka consumer with AWS MSK IAM auth: re-assume the STS role on each SASL re-auth instead of reusing cached credentials, fixing periodic
ILLEGAL_SASL_STATEerrors during re-authentication, see #1146 by @samir-is-here which fixes #1144. - Fix unidirectional subscribe stream proxy not closing on unsubscribe, see #1150.
Miscellaneous
- This release is built with Go 1.26.4
- Dependency updates
- This is the latest release that ships
linux/386binary - See also the corresponding Centrifugo PRO release.
v6.8.1
Centrifugo is an open-source scalable real-time messaging server. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE/EventSource), GRPC, WebTransport). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server.
Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.
Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.
For details, go to the Centrifugo documentation site.
What's changed
Improvements
- Support Prometheus native histograms, see #1136. See in docs. Summaries are now DEPRECATED in Centrifugo. Metrics which only had summary now have histogram analogue exposed.
- Kafka consumer: don't re-init the client on retriable fetch errors, see #1137. Should improve stability of consumer during temporary issues with Kafka and prevent unnecessary rebalances.
Fixes
- Add missing
envconfigtags to NATS JetStream consumer config so its fields can be configured via environment variables, see #1117 by @thuy-le-kafi. Also applied the same fix to the Redis Streams and Azure Service Bus consumer configs, which had the same gap. - Fix: Scope JWKS cache by resolved endpoint URL #1142. Fixes GHSA-g6vg-wj8f-48cj reported by @sondt99
- Fix a bunch of flaky integration tests.
Miscellaneous
- This release is built with Go 1.26.3
- Dependency updates
- See also the corresponding Centrifugo PRO release.
v6.8.0
Centrifugo is an open-source scalable real-time messaging server. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE/EventSource), GRPC, WebTransport). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server.
For details, go to the Centrifugo documentation site.
What's changed
This release introduces two new subscription primitives and three Postgres-backed components. Centrifugo evolves from a channel-based pub/sub server into a real-time companion for Postgres-backed applications. From a stream based broker to a state synchronization realtime backend.
A blog series accompanies this release. Two posts are published today:
- Shared poll subscriptions: O(unique items) polling with low-latency updates introduces one of the new subscription types,
- Recreating a Two Million Particle World at 30 Hz with Centrifugo puts shared poll to work reproducing David Gerrells' particle simulation on top of Centrifugo's WebSocket protocol.
Five more posts will follow during this week, covering map subscriptions, the PostgreSQL stream broker, the PostgreSQL controller, and a wrap-up tying it all together:
- Day 2: Map subscriptions (Part 1) — synchronized key-value state and presence
- Day 3: Map subscriptions (Part 2) — when your PostgreSQL transaction is your real-time publish
- Day 4: Transactional publishing for stream subscriptions with PostgreSQL
- Day 5: Multi-node Centrifugo on PostgreSQL alone
Note, we consider map subscriptions and shared poll experimental for now. Features are huge and we expect API to stabilize within next months.
Highlights of release
- Map subscriptions — new subscription type for synchronized key-value collections (cursors, presence, scoreboards, dashboards, feature flags).
- Shared poll subscriptions — new subscription type for read-only state where the backend doesn't control writes (third-party feeds, configuration sync, live counters).
- PostgreSQL stream broker — transactional publishing for stream subscriptions inside your SQL transactions.
- PostgreSQL map broker — transactional publishing for map subscriptions inside your SQL transactions.
- PostgreSQL controller — multi-node Centrifugo clusters on PostgreSQL alone, no Redis required.
Yes, Centrifugo multi-node cluster only on PostgreSQL is now possible.
Map subscriptions
A new subscription type that delivers synchronized key-value collections over the same WebSocket connection. Centrifugo keeps the collection in the map broker; the client stays in sync.
Three modes for different state shape:
ephemeral— usually memory or Redis backed, no recovery on reconnect (cursors, typing indicators, in-flight game state).recoverable— time-bounded recovery from stream within a window, state converges with the help of offset/epoch mechanics.persistent— durable, no per-key TTL — entries live until explicitly removed (feature flags, configuration, catalog state).
Key features:
- Three-phase sync protocol resolves the race between paginated initial state and concurrent updates.
- Per-key TTL handles cleanup on disconnect.
- CAS-style conditional writes handle concurrent updates safely.
- Convergence on reconnect guaranteed by offset/epoch tracking (in recoverable and persistent modes).
Docs: Map subscriptions
Cursors demo: Map Cursors
More Map Demos (with PostgreSQL map broker): v6/map_demo collection — cursors, polls, sprint board, scoreboard, inventory, lobby, tickers, sync protocol visualizer.
Map presence
Map subscriptions introduce a new presence model built on the same sync protocol. Two server-managed subscription types replace the polling-style traditional presence API for use cases that need it:
map_clients— one entry per connection (client presence).map_users— one entry per user (user presence).
Enabled via map_clients_presence_channel_prefix and map_users_presence_channel_prefix on the parent namespace. Presence is published to a separate channel (prefix + channel), so subscribers opt in independently of the parent subscription. Works alongside any parent subscription type (stream, map, shared_poll).
Advantages over traditional presence:
- Paginated state delivery for large participant lists.
- Stream-based catch-up on reconnect (with
recoverablemode) — clients receive only the join/leave changes they missed, no full re-fetch. - Out of the box convergence (must be in recoverable mode for convergence property)
Docs: Presence channels
Demo: v6/map_presence_demo — 100k and 1M presence members synchronized to a single browser tab
Shared poll subscriptions
Server-side polling inversion for read-only state where the backend doesn't control writes. Instead of N clients each polling the backend, Centrifugo aggregates interest across all clients on a node and polls the backend once per cycle for the union of tracked keys. Backend load scales with O(unique items tracked), not O(connected clients).
Key features:
- Per-key authorization via HMAC signatures (faster than JWT, first-class values in protocol).
- Direct publish via
shared_poll_publishlowers latency on application write paths. - Versionless and versioned modes for flexible backend integration.
- Cold-key auto-poll: when a new client tracks a key with no cached data, Centrifugo triggers an immediate backend poll without waiting for the next cycle.
- Reconnect resilience with version comparison: clients reconnect, replay tracked keys, only changed data re-delivered.
Docs: Shared poll
Blog: Shared poll subscriptions: O(unique items) polling with low-latency updates
Demos: v6/shared_poll_demo — votes, drones
PostgreSQL stream broker
Transactional publishing for stream subscriptions. The cf_stream_publish SQL function commits inside your application's SQL transaction — your INSERT/UPDATE and the real-time publish commit together. If the transaction rolls back, the real-time update never happened. No application-side outbox to maintain, no CDC pipeline.
Architecture:
- Partitioned outbox table (
cf_stream) with daily date ranges. - LISTEN/NOTIFY for low-latency wakeup, polling for correctness.
- Drop-partition cleanup — vacuum-free at scale.
- Two independent TTLs:
HistoryTTL(queryable history) andHistoryMetaTTL(channel epoch). HistorySizeenforced at read time, not write time.
Pairs with the SDK's getState callback for the app-owned state pattern: clients load initial state from your application API, then subscribe from the captured stream position. The SDK switches paths automatically — replays from stream history on normal reconnects, calls getState only when the gap exceeds what history covers.
Docs: Engines — PostgreSQL Broker
Example: v6/pg_stream_broker — kitchen orders, multi-tenant per-restaurant channels
PostgreSQL map broker
Transactional publishing for map subscriptions. The cf_map_publish and cf_map_remove SQL functions commit inside your application's SQL transaction — write your row, publish the map update, both atomic.
Same outbox-on-Postgres foundation as the stream broker — partitioned outbox table, LISTEN/NOTIFY wakeup, daily partition retention, vacuum-free cleanup.
The map broker stores cf_map_state and cf_map_stream in your PostgreSQL database. The state lives in cf_map_* tables that Centrifugo owns, queryable from psql. Best fit when Centrifugo is the natural store for the data — feature flags, IoT device fleet telemetry, lobby rosters, collaborative cursors.
Docs: Map subscriptions — PostgreSQL
PostgreSQL controller
The control-message bus for multi-node Centrifugo (subscribe propagation, disconnects, presence pings, surveys) can now run on PostgreSQL — the same outbox-on-Postgres foundation as the brokers. Until this release, multi-node OSS Centrifugo required Redis for that bus.
With the stream broker, map broker, and controller all on PG, an OSS cluster runs with PostgreSQL as the only messaging-plane dependency. No Redis, no NATS, no separate pub/sub broker.
For applications already running Postgres for everything else, the messaging plane has what it needs.
Docs: [PostgreSQL controller](https://centr...
v6.7.2
Centrifugo is an open-source scalable real-time messaging server. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE/EventSource), GRPC, WebTransport). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server.
Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.
Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.
For details, go to the Centrifugo documentation site.
What's changed
Miscellaneous
- This release is built with Go 1.26.3
- Dependency updates
- See also the corresponding Centrifugo PRO release.
v6.7.1
Centrifugo is an open-source scalable real-time messaging server. Centrifugo can instantly deliver messages to application online users connected over supported transports (WebSocket, HTTP-streaming, Server-Sent Events (SSE/EventSource), GRPC, WebTransport). Centrifugo has the concept of a channel – so it's a user-facing PUB/SUB server.
Centrifugo is language-agnostic and can be used to build chat apps, live comments, multiplayer games, real-time data visualizations, collaborative tools, etc. in combination with any backend. It is well suited for modern architectures and allows decoupling the business logic from the real-time transport layer.
Several official client SDKs for browser and mobile development wrap the bidirectional protocol. In addition, Centrifugo supports a unidirectional approach for simple use cases with no SDK dependency.
For details, go to the Centrifugo documentation site.
What's changed
Improvements
- Kafka consumer now supports AWS STS AssumeRole for MSK IAM authentication via the new
consumers[].kafka.assume_role_arnoption, #1129 by @samir-is-here. When set together withsasl_mechanism: "aws-msk-iam", Centrifugo loads base credentials via the AWS SDK default credential chain and assumes the specified IAM role to obtain temporary credentials with automatically refreshed session tokens. This is useful for cross-account MSK access or when running Centrifugo with an EC2/EKS/ECS instance profile. Staticsasl_user/sasl_passwordkeys remain the default whenassume_role_arnis empty. See documentation.
Fixes
- CI fix: set LocalStack image version to 4.14 in development setup, #1119.
Miscellaneous
- This release is built with Go 1.26.2
- Dependency updates
- See also the corresponding Centrifugo PRO release.