symfony/mercure-bundle
Symfony bundle that integrates the Mercure Component into full-stack Symfony applications, making it easy to add real-time push updates over Server-Sent Events. Provides configuration, dependency injection setup, and seamless hub integration for publishing and subscribing to live updates.
Hub vs. FrankenPhpHub at runtime via a factory, and an empty url now reliably selects FrankenPHP's built-in hub. That makes "single-binary app + embedded hub" (FrankenPHP worker mode, Docker Compose with an unset MERCURE_URL) a credible low-infra option versus running a separate hub — a real cost/ops tradeoff we can now decide per environment instead of per codebase.url in the default aud claim — and that claim is what the hub actually reads. Practically: a hub reachable as http://hub:80 inside the cluster but https://realtime.example.com publicly now accepts publications without the resource_identifier workaround we previously had to configure by hand. This removes a standing piece of bespoke config from any containerized/multi-network deployment and makes the "separate hub behind a different public hostname" topology a config-free default rather than a maintenance liability.http_client (e.g. a scoped client with a short timeout) lets us isolate slow or flaky hubs from request latency; autowirable RemoteHubInterface (globally for the default remote hub, per hub via named arguments like RemoteHubInterface $fooHub) enables multi-region or per-tenant hubs without boilerplate.publisher and subscriber options allow signing publisher and subscriber tokens with different keys, enabling least-privilege token issuance and cleaner key-rotation stories than a single shared jwt secret. Note that 0.6.1 broadens the default publisher aud to the hub URL — if we ever rely on audience as a restriction (rather than as the hub-selection hint it is here), we should set the claim explicitly instead of relying on the default.Publisher/PublisherInterface/TraceablePublisher and StaticJwtProvider services removed; MercureDataCollector::getPublishers() removed) plus a hard config requirement for jwt.secret hubs. 0.6.1 is a patch release with no breaking changes — it only adds url to the default aud claim, so it is safe to take inside the same migration window. Still pin the bundle version in composer.json rather than floating it; the aud default is behavior, and behavior in a pre-1.0 package can shift again. As a cleanup item: if we added resource_identifier to publishers solely to work around internal/public URL mismatch, we can remove it after upgrading and verifying against our hub.symfony/mercure client (^0.9 supported here) directly — you get the hub/token plumbing without the bundle.protocol_version: 0.x), your code uses the Hub/HubInterface API (not the removed Publisher API), and any jwt.secret hub config defines iss, sub, and client_id in jwt.claims — otherwise the container fails to compile, which is a deploy-time failure, not a runtime one.resource_identifier needed.protocol_version: 0.x override long term. (The 0.6.1 aud change applies to protocol 1.0 publisher tokens only, so 0.x overrides see no behavior change.)Publisher/StaticJwtProvider APIs and cannot absorb the migration now — pin the current version and defer, but do not assume the deprecation-free path exists forever.aud strictly against a fixed custom value rather than the hub URL — verify before upgrading, and override the claim explicitly if so.__Secure-mercure_access_token, and mercure_access_token under kernel.debug).For Executives: "MercureBundle lets us deliver real-time updates—live notifications, dashboards, collaborative features—without the cost and complexity of WebSockets. The 0.6.x line is a maturity milestone: it defaults to the now-final Mercure 1.0 protocol, so we're building on a stable standard rather than a draft. It also lets us collapse infrastructure: an empty hub URL reliably runs Mercure inside FrankenPHP, so we can ship real-time as part of the app binary instead of standing up and operating a separate service. Operationally we gain per-hub HTTP clients (so one slow hub can't stall requests) and separate signing keys for publishers and subscribers (better least-privilege and rotation). The 0.6.1 patch additionally removes a piece of bespoke configuration we used to need whenever the hub's internal network address differed from its public hostname — fewer environment-specific knobs, fewer ways for a deploy to be subtly wrong. The cost is a one-time migration: 0.6.0 removed the old Publisher APIs and requires explicit JWT claims in config. That's a scheduled, contained piece of work — worth doing now while the surface is small, and it clears the deprecated code paths that were firing warnings on every boot. MIT-licensed, Symfony-backed, low risk."
For Engineers:
*"0.6.1 is a one-line patch on top of 0.6.0 — no BC breaks. It adds the hub url to the default aud claim of protocol 1.0 publisher tokens, which is where those tokens are actually sent. Concretely: if the hub is addressed internally (http://hub:80) but publicly known as something else, publications are accepted without setting resource_identifier. Upgrade, then delete that override if you added it for this reason, and re-run your publish path against the real hub topology. If any hub-side validation pins aud to a custom value, set the claim explicitly.
Everything from 0.6.0 still applies. What you get:
protocol_version: 0.x to stay on an old hub; also accepts ProtocolVersion enum cases.Hub vs FrankenPhpHub chosen by a factory, not at container build time. Empty url (unset MERCURE_URL, all Compose can express) reliably selects FrankenPHP's hub, autowirable via HubInterface. url/public_url default to null unconditionally.http_client — publish through a dedicated/scoped client with a short timeout instead of the global one.RemoteHubInterface — globally for the default remote hub, per hub via named arguments.publisher/subscriber — separate signing keys as an alternative to a single jwt.symfony/mercure ^0.9 support.default_hub pointing at an undefined hub now raises a config error instead of a TypeError; TraceableHub no longer accumulates previous requests' messages in long-running processes (FrankenPHP worker mode).Breaking changes to plan for (from 0.6.0; 0.6.1 adds none):
Publisher / PublisherInterface / TraceablePublisher services and aliases, plus StaticJwtProvider (mercure.hub.*.jwt_provider), are gone. Migrate to Hub / HubInterface / TraceableHub and StaticTokenProvider (mercure.hub.*.jwt.provider). Side benefit: no more symfony/mercure 0.5 deprecations on every container boot.MercureDataCollector::getPublishers() removed — use getHubs().jwt.secret hubs must declare iss, sub, client_id in jwt.claims, or container compilation fails.__Secure-mercure_access_token (mercure_access_token when kernel.debug, matching the hub's playground default). Audit any custom subscriber clients or integration tests that read the cookie name.Other tradeoffs: not for bidirectional comms; requires HTTP/2 (falls back gracefully); hubs may be third-party (self-hosting available); pre-1.0 versioning means more BC breaks ahead — pin the version. The 0.6.1 aud default is convenient but is behavior, not contract; don't build authorization logic on top of it.
Next steps:
Publisher/PublisherInterface/TraceablePublisher/StaticJwtProvider, add required jwt.claims, compile the container (this is where failures surface).protocol_version: 0.x as a documented stopgap.resource_identifier overrides that existed only to bridge internal/public hub URLs, and verify publishing still succeeds against the deployed hub topology.http_client timeouts where hub calls are on a request path.How can I help you explore Laravel packages today?