denis660/laravel-centrifugo
Laravel integration for Centrifugo real-time messaging: broadcasting driver, HTTP API client, JWT authentication, channel subscriptions, presence and publish helpers. Easily add scalable WebSocket updates to Laravel apps.
This package wires Laravel's broadcasting system to a Centrifugo server. The v5.3 release is a good entry point: it fixes the broadcaster auth token to use the authenticated Laravel user id, fixes the CentrifugoInterface container binding, and normalizes transport failures into BroadcastException.
Install the server and the package:
composer require centrifugal/centrifuge-laravel
php artisan centrifuge:install
The improved centrifuge:install now handles Laravel 9–13 app structures (older skeleton layouts, bootstrap/app.php vs Http/Kernel.php), so run it before hand-editing config.
Configure the connection. Publish config/centrifuge.php and set:
CENTRIFUGO_URL (HTTP API endpoint, e.g. http://localhost:8000)CENTRIFUGO_API_KEY (server-to-server publish key)CENTRIFUGO_SECRET (HMAC token secret — this is the one used to sign connection/subscription tokens)BROADCAST_CONNECTION=centrifugo in .envBring up a Centrifugo instance (the release adds real Docker-based integration checks, and the docs now show a working compose file). Then make your first broadcast:
broadcast(new OrderShipped($order))->toOthers();
First use case: a private per-user channel (private-orders.{userId}) fed by an event implementing ShouldBroadcast, with centrifuge-js on the client subscribing and hitting /broadcasting/auth for the subscription token. Verify the token's sub claim matches the Laravel user id — that's exactly the bug v5.3 fixed.
Event-driven broadcasting. Standard Laravel flow: implement ShouldBroadcast/ShouldBroadcastNow, return PrivateChannel/PresenceChannel instances from broadcastOn(). The package signs subscription tokens via the broadcasting auth route; keep your channel authorization callbacks in routes/channels.php and make sure the guard you authorize against matches config/auth.php defaults.
Direct server-side publishing. With the container binding fixed, resolve the client directly for non-event use (notifications, progress ticks, cache invalidation):
use Centrifugo\CentrifugoInterface;
public function __construct(private CentrifugoInterface $centrifugo) {}
$this->centrifugo->publish('private-orders.42', ['status' => 'shipped']);
This is the pattern to reach for when you don't want an event class or a queue hop.
Queue your broadcasts. Use ShouldBroadcast (queued) rather than ShouldBroadcastNow for anything user-triggered; the HTTP call to Centrifugo is a network round-trip and will otherwise sit in the request path.
Client integration. On the frontend, create the Centrifuge client, obtain a connection token from a small endpoint ($user->createToken(...)-style or the package's token helper), then subscribe to channels — getToken on the subscription should call /broadcasting/auth. The v5.3 E2E test does a real private subscribe with centrifuge-js, so the documented flow is verified end to end.
Error handling. Wrap publishes/broadcasts in try/catch (BroadcastException $e). Since transport failures now surface as BroadcastException instead of arbitrary Guzzle exceptions, you can centralize retry/logging logic and let Laravel's queue retry semantics apply.
Testing. Use Broadcast::fake() / Event::fake() for unit tests, and point the config at a throwaway Centrifugo container for integration tests — mirror the shipped Docker-based checks rather than mocking the HTTP layer if you care about token correctness.
CENTRIFUGO_SECRET (HMAC token_hmac_secret_key) signs tokens; CENTRIFUGO_API_KEY authenticates publish calls. Swapping them produces 401s from the API and "invalid token" on the client with no obvious link between the two.sub claims, impersonation, API-token auth), re-verify private channel authorization — the fix may change which user id ends up in the token, especially under non-session guards.CentrifugoInterface could fail to resolve. If you have a hand-rolled binding in a service provider, remove it; the package binding now wins and duplicates can mask config issues.BroadcastException, not Throwable. Transport normalization means connection refused, timeouts, and non-2xx responses all arrive as BroadcastException. Queued broadcasts that fail will retry — make sure your publish payloads are idempotent, since a retried broadcast can deliver twice.php artisan config:clear after editing .env; the install command writes config, and stale caches are the most common "it works in tinker but not in the app" cause.private- prefix on the client and in broadcastOn(); presence channels use presence-. Client-side subscriptions to the unprefixed name will silently never authorize.allowed_origins, token_hmac_secret_key, and API options changed across Centrifugo 4/5/6. The package's Docker fixtures show a known-good pairing — copy that compose file verbatim before debugging your own.How can I help you explore Laravel packages today?