Releases: centrifugal/centrifugo
Release list
v6.9.6
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
- A client-side subscription refresh with an already expired subscription token was accepted. The subscription's expiration time was then cleared, so the subscription never expired. Now Centrifugo closes the connection with the
3006(subscription expired) disconnect code. The client reconnects, gets a token expired error on resubscribe and requests a new token – all official SDKs already handle this (centrifugal/centrifuge#627). - Fossil delta compression with recovery: when a subscribe with recovery found no missed publications, the next publication was still sent as a delta. The client had no base data for it, so it could not decode that publication or any after it. Now the first publication after such a subscribe is sent with full data (centrifugal/centrifuge#629).
- Align fossil deltas to UTF-8 character boundaries for JSON clients centrifugal/centrifuge#630. For a change inside a multi-byte character it copies the first bytes of the character from the previous data and inserts the rest. The inserted bytes aren't valid UTF-8, json.Escape replaces them with U+FFFD, and the client fails to apply the delta. The fix eliminates this.
Miscellaneous
- This release is built with Go 1.26.8.
- Dependency updates.
- See also the corresponding Centrifugo PRO release.
v6.9.5
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
- Slightly less work when recording
centrifugo_client_ping_pong_duration_seconds– the histogram observer is now cached instead of being looked up on every pong.
Fixes
- Fix possible panic in the channel options cache on long-running nodes. Its internal counter overflowed
int32after enough cache misses, which gave a negative slot index and crashed the node withindex out of range(#1227). - Map subscriptions: the subscribe reply did not include
expires/ttl, so clients could not refresh the subscription token in time. Also, if the map state was loaded in several pages, the client's subscription refresh was rejected with abad requestdisconnect. A subscription whose expiration time was already in the past was accepted (centrifugal/centrifuge#626). allowed_originspatterns with upper-case letters (e.g.https://App.Example.com) never matched, because the requestOriginwas lower-cased before matching and the pattern was not. Patterns are now matched case-insensitively (#1230).client_namewas ignored for Redis used byredis_streamasync consumers (#1229).- PostgreSQL engine: handle one more case of several nodes creating the schema at the same time – a node could fail on start with
type ... already exists(SQLSTATE42710) (#1231). - The
centrifugo_client_subscriptions_acceptedmetric was registered but never incremented, so it always showed zero (centrifugal/centrifuge#622). - The
client closed or unsubscribed after adding subscriptionmessage is now logged atinfolevel instead oferror. It happens when a client goes away while its subscription is still being set up – this is expected and is not a server error (centrifugal/centrifuge#621).
Miscellaneous
- We’ve updated the official Centrifugo dashboard. It covers all exposed metrics in v6.9.4, avoids deprecated Prometheus summaries, and includes descriptions for all panels. You can download it from grafana.com. See also Centrifugo's observability docs
- This release is built with Go 1.26.8.
- Dependency updates.
- See also the corresponding Centrifugo PRO release.
v6.9.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
Improvements
- New
centrifugo_transport_frame_sizemetric – a histogram of frame sizes received from client connections. One frame may contain several commands, so this can't be calculated from the existing per-command counters. Helps to choose a good value forwebsocket.message_size_limit, which is applied to the whole frame (centrifugal/centrifuge#620). - Faster Protobuf client protocol command encoding: ~2.1x faster and ~55% less memory alloc
- Less work on every client command: config accessors and the channel options cache no longer copy structs on each call (#1215).
- PostgreSQL broker, map broker and controller now validate
table_prefixon start. A prefix with a hyphen, space or dot used to produce a confusing syntax error inside a generatedCREATEstatement (#1223).
Fixes
- Async consumers: a
context.Cancelederror returned by the message dispatcher was treated as a shutdown signal. In the Kafka consumer this could skip records and then commit an offset after them, losing messages. This did not happen right now in Centrifugo, but inaccurately introduced code could cause this in the future. Fixed for Kafka and SQS (#1221). - fix(pgoutbox): tolerate 42P07 when another node creates the partition first #1220. Thanks to @AlexeyShalaev for bringing and fixing this.
- PostgreSQL engine: handle the case when several nodes create the schema at the same time. Before that a node which lost the race could fail to start with
error initializing Postgres ... schema. - PostgreSQL map broker: the fast path in
EnsureSchemanever worked, so every node start ran the full DDL batch again. It works now and also refreshes the partition lookahead (#1223). - Fix a race in log throttling of the connection limit check – it could print many duplicate warnings instead of one per throttle interval (#1214).
- Some config validation errors mentioned keys which don't exist, so it was hard to find the option to fix (#1213).
- Admin web UI: fix uptime formatting on the Status page (centrifugal/web#71).
Miscellaneous
- This release is built with Go 1.26.8.
- Dependency updates.
- See also the corresponding Centrifugo PRO release.
New Contributors
- @AlexeyShalaev made their first contribution in #1220
Full Changelog: v6.9.3...v6.9.4
v6.9.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
- Faster and less allocating decoding of client commands. The command decoders are on the hot path – one is taken from the pool for every frame a client sends. The JSON decoder now reuses its buffered reader instead of allocating a new 4KB buffer per frame, and returns commands without allocating a slice per command when they fit into the read buffer. The Protobuf decoder unmarshals small messages straight out of the buffered reader instead of copying them into a scratch buffer first. Decoding a frame with a single JSON command got 62% faster with 93% less memory allocated, a frame with 8 commands – 25% faster with 67% less memory; for Protobuf the win is around 7-10% in time (centrifugal/protocol#41).
- The message size limit is now applied to every command in a JSON frame. Previously it was only checked reliably for the first command, so subsequent commands in the same frame could exceed the configured limit (up to roughly twice it). A command of exactly the limit is still accepted wherever it sits in the frame (centrifugal/protocol#41).
Fixes
- Fix
client.connection_rate_limit: the configured value was used as the token bucket burst only, while the sustained rate was one connection per second. Centrifugo now accepts the configured number of new connections per second, keeping the configured value as the burst size too. Thanks to @moses-mbaga for the fix (#1207). - Shared poll:
hmac_previous_secret_key_valid_untilwas compared against theiatclaim carried inside the track signature. That value is chosen by whoever mints the signature, so anyone retaining the rotated-out key could keep producing accepted signatures indefinitely by backdatingiat. The cutoff is now enforced against server time before consulting the previous verifier – the same way it works for the previous JWT HMAC key – with theiatcheck kept as an additional constraint inside the grace period (#1209). - Apply
publication_data_formaton all publication paths. It was only enforced for stream publishes (API publish, API broadcast, client publish), so map publish, shared poll publish and client map publish stored whatever bytes the caller sent. Data returned by publish, map publish and shared poll refresh proxies is now validated as well – a proxy can replace the data a client sent, and a shared poll refresh result reaches subscribers without ever passing through a client (#1210).
Miscellaneous
- This release is built with Go 1.26.7.
- Dependency updates.
- See also the corresponding Centrifugo PRO release.
New Contributors
- @moses-mbaga made their first contribution in #1207
Full Changelog: v6.9.2...v6.9.3
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.