Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Centrifugo Bundle Laravel Package

fresh/centrifugo-bundle

PHP bundle that integrates the Centrifugo real-time messaging server into Symfony apps. Configure connections, issue JWT client tokens, publish messages to channels, and add websocket-powered live updates, notifications, and presence features with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

fresh/centrifugo-bundle is a thin Symfony integration for Centrifugo: it registers the framework-agnostic centrifugo/centrifugo-php HTTP client as a service and feeds it your API URL, API key, and JWT secret. That's essentially the whole package — the value is configuration plus DI wiring, not new behaviour.

A Laravel developer should be upfront about the fit: this will not install "usefully" into a Laravel app. There is no auto-discovery, no service provider, no Facade. You can use the underlying client directly — and that's what you should do — while treating the bundle as a reference implementation for config keys and token semantics. Reading DependencyInjection/Configuration.php, DependencyInjection/*Extension.php, and the service definitions tells you everything the bundle does in about five minutes.

Minimal path in a Symfony app (the intended use):

  1. Run Centrifugo (Docker is easiest) with api_key and a token HMAC secret configured; they must match your app config.
  2. composer require fresh/centrifugo-bundle.
  3. Configure config/packages/fresh_centrifugo.yaml with api_url (include the /api path), api_key, and secret.
  4. Inject the client into a service and call publish().

The Laravel equivalent (same client, same semantics):

// AppServiceProvider::register()
$this->app->singleton(\Centrifugo\Centrifugo::class, fn () => new \Centrifugo\Centrifugo(
    config('services.centrifugo.api_url'),   // e.g. http://centrifugo:8000/api
    config('services.centrifugo.api_key'),
    config('services.centrifugo.secret'),
));

First use case worth building: a GET /realtime/token endpoint that returns a connection JWT for the authenticated user, plus a single publish call after a domain event (order.updated, message.created). That covers 80% of real Centrifugo usage.

Implementation Patterns

Config + client, nothing more. Keep the bundle's key names (api_url, api_key, secret) in your Laravel config/services.php so the mental model and docs map 1:1 if you ever move between frameworks.

Connection tokens at the edge. Generate the JWT in a controller after auth, not in the model layer:

$token = $client->generateConnectionToken((string) $user->id, time() + 3600, [
    'name' => $user->name,
]);
return response()->json(['token' => $token]);

sub should be a string. Pass info as a JSON string in the underlying client (it's embedded in the token, not the payload).

Subscription tokens for private channels. Public channels need nothing; private ones need a JWT scoped to channel + sub. Endpoint per channel (or a whitelist check) is the standard pattern:

$client->generateSubscriptionToken((string) $user->id, "user:{$user->id}", time() + 3600);

Channel naming is the architecture. Adopt a convention early: user:{id} for per-user fanout, chat:{roomId}, presence:{tenant}. Configure matching namespaces in Centrifugo (namespaces with presence, history_size, force_recovery) rather than inventing ad-hoc names — recovery/history only work if the namespace is configured server-side.

Publish from events/listeners, after commit. The single most common bug is publishing inside a DB transaction that later rolls back, leaving clients with ghost state. Use DB::afterCommit() (or a queued listener) and publish from there.

$client->publish([
    'channel' => "user:{$order->user_id}",
    'data'    => ['type' => 'order.updated', 'id' => $order->id, 'status' => $order->status],
]);

Prefer broadcast over N publishes when the same payload goes to many channels — one HTTP round-trip instead of dozens. For per-user fanout to thousands of users, loop broadcast in chunks, not publish per user.

Wrap it behind your own interface. Define RealtimePublisher with publish(), broadcast(), token(). Bind the Centrifugo client behind it. This gives you a fake for tests, a seam for swapping providers, and insulation from the bundle's (low) release cadence.

Testing. Bind a fake/mock of your interface in the container and assert the payload shape rather than the HTTP call. If you test the client itself, point api_url at a stub HTTP server and assert X-API-Key plus JSON body.

Frontend pairing. Use centrifuge-js v5: new Centrifuge(url, { getToken }) where getToken calls your token endpoint; subscriptions get their own getToken for private channels. Tokens expire — the client will re-request, so your endpoint must be cheap and idempotent.

Gotchas and Tips

  • api_key ≠ secret. The API key authenticates HTTP API calls (X-API-Key header). The secret signs JWTs. Mixing them up produces "unauthorized" on publish and "invalid token" on connect, which look similar but have different causes. Both must match Centrifugo's config exactly.
  • Centrifugo config key drift. Older versions use root-level token_hmac_secret_key; v5+ nests it under client.token.hmac_secret_key. Confirm against the server version you're running before debugging your PHP side.
  • api_url must include /api. The client appends the method path; a missing /api yields 404s that look like auth failures. Also avoid a trailing slash.
  • API errors are returned, not thrown. The PHP client returns a response object; an invalid channel or bad key often surfaces as ->error on the result rather than an exception. Check it explicitly, or wrap the client so it throws — otherwise failures are silent.
  • sub is a string, and info is a JSON string. Passing an int ID or an array for info produces tokens that Centrifugo rejects at connect time, not at generation time. Cast and json_encode at the boundary.
  • Channels must be allowed server-side. Centrifugo enforces allowed_channels/namespaces. A publish to an undefined channel can fail or silently vanish depending on version — configure namespaces first, then name channels.
  • Publish after commit, not inside transactions. Covered above, but it's the #1 real-world bug with any Centrifugo integration.
  • No queue, no retry, no batching. The bundle is a client wrapper. If real-time delivery matters, publish from a queued job with retry/backoff and accept at-most-once semantics for the HTTP hop.
  • Laravel-specific: config caching means your services.centrifugo values are frozen at config:cache; restart workers after env changes. Don't call the client in a service provider's boot() (network on every request). Use a singleton binding and resolve lazily.
  • Vendor risk. 84 stars and zero dependents means you are the integration's maintainer in practice. Pin the version, keep the client behind your own interface, and treat upgrades as manual work.
  • Extension points (Symfony): decorate the client service, alias your own implementation, or supply a custom HTTP client for timeouts/proxies. In Laravel, the equivalent is binding your own instance in the container — which is also the cleanest way to add timeouts and logging.
  • Debugging loop: curl -X POST http://centrifugo:8000/api -H "X-API-Key: ..." -d '{"method":"publish","params":{"channel":"test","data":{}}}' isolates server/config problems from PHP problems in one step. Check Centrifugo logs for rejected tokens before touching application code.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels