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

Mercure Bundle Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture Fit

  • Package Nature Clarification: The released artifact remains symfony/mercure-bundle (now v0.6.1), not a Laravel package. For a Laravel/PHP target stack, the realistic integration surface is the framework-agnostic symfony/mercure client (permitted at ^0.9) wired through a Laravel service provider — the bundle's autowiring, config tree, and FrankenPHP factory logic are Symfony-kernel features that do not carry over. Value is moderate for Laravel, high for Symfony services in a mixed estate.
  • Real-time Push Capabilities: Mercure remains a strong fit for server-sent updates (live notifications, dashboards, collaborative views) using plain HTTP/SSE rather than bespoke WebSocket infrastructure. The 1.0 protocol default introduced in v0.6.0 is unchanged in v0.6.1 — the transport contract remains stable and documented, which is what matters for a TPM sizing long-lived commitments.
  • Architectural Shift — Runtime Hub Selection: v0.6.0 chooses between Hub and FrankenPhpHub at runtime via a factory, with url/public_url defaulting to null unconditionally. Good for Docker Compose / 12-factor deployments and enables FrankenPHP worker mode; but a missing env var silently selects the embedded hub instead of failing loudly. Unchanged in v0.6.1 and still a misconfiguration class we must guard against.
  • Dual-URL Topologies Now First-Class (v0.6.1): The patch adds url to the default aud claim of protocol 1.0 publisher tokens. Practically, this means a hub reached at an internal URL (app → hub) while exposed at a different public URL (browser → hub) no longer requires the resource_identifier workaround to accept publications. This is a direct follow-through on the null-default/hub-factory shift in v0.6.0: the framework now models the split-URL reality most containerised deployments actually have. Architectural fit improves for our standard deployment shape; no design change required.
  • Decoupled Event Flow: Publish/subscribe separation still pairs naturally with queued jobs (Laravel queues / Symfony Messenger), keeping request latency independent of fan-out.

Integration Feasibility

  • Laravel Friction (Medium): composer require symfony/mercure:^0.9 plus a singleton binding for HubInterface / RemoteHubInterface built from config('mercure.*'). Accept no bundle config tree, no autowiring, no profiler panel — we own the wiring. Estimated 0.5–1 day. v0.6.1 does not change this effort; if anything it removes one config branch (resource_identifier) from the provider.
  • Symfony Friction (Low, post-migration): RemoteHubInterface is autowirable — globally for a remote default hub, and per-hub via named arguments (RemoteHubInterface $fooHub). Per-hub publisher / subscriber options allow distinct signing keys.
  • Publish-Path Reliability: The http_client per-hub option lets us bind a dedicated, short-timeout, connection-pooled HTTP client for publishing, isolating Mercure publish latency from the app's default HTTP client. Unchanged by v0.6.1 and still the highest-value runtime lever in the release line.
  • Claim Defaults Reduced to One: v0.6.0's hard requirement was iss / sub / client_id in jwt.claims for jwt.secret hubs. v0.6.1 removes aud from the list of claims we must hand-configure for dual-URL hubs — the default now carries the hub url. Net effect: less bespoke JWT configuration per environment, which is where most of the upgrade cost sat.
  • Protocol Coupling: Default protocol_version: 1.0 means hub and client must move together. The aud-includes-url behaviour is a 1.0-only change; hubs pinned to 0.x via the bridge flag will not see it. The escape hatch (protocol_version: 0.x, or the ProtocolVersion enum) remains a short-lived bridge.
  • Hub Agnostic: Self-hosted (Docker/Caddy) and managed hubs both remain viable; hub clustering/scale characteristics are unchanged by this patch.

Technical Risk

  • HIGH — JWT Claims Still Fail Container Compilation (carried from v0.6.0): For hubs using jwt.secret, iss, sub, and client_id must be present in jwt.claims or the container fails to compile. Hard boot-time failure, not a runtime warning. Still the single largest risk in this release line and still mandates a per-environment config audit before deploy.
  • HIGH — Breaking Removal of Legacy Publisher Services (carried from v0.6.0): v0.6.0 stops registering Publisher / PublisherInterface / TraceablePublisher and StaticJwtProvider; consumers must move to Hub / HubInterface / TraceableHub and StaticTokenProvider. Any injected PublisherInterface becomes an unresolvable-service error at boot. v0.6.1 does not restore or alias any of these — the rename remains mandatory for anyone upgrading from v0.4.x.
  • LOW-MEDIUM — aud Claim Behaviour Change (NEW in v0.6.1): The default aud for protocol 1.0 publisher tokens now contains the hub url. Risks are narrow but real:
    1. Hub-side aud validation: a hub configured to expect a specific aud (e.g. public URL, or a custom resource_identifier-derived value) may now receive a token with an additional/different aud entry. aud is a list, so permissive hubs are unaffected; strictly-scoped hubs must be checked before rollout.
    2. Interaction with explicit config: confirm whether an explicitly configured aud in jwt.claims overrides, merges with, or is appended to the new default. Assume merge until verified — that is the safest planning assumption and the one that could surface duplicate or unexpected audience entries.
    3. url: null case: with v0.6.0's unconditional null default, confirm the aud claim is omitted or empty rather than containing a null/empty value. An aud of "" would be rejected by most hub validators. No boot-time failure class here; failure mode is 401 on publish at runtime, which is cheaper to detect but easy to miss in staging if staging shares a single URL for internal and public access.
  • MEDIUM — Cookie Contract Change for Subscribers (carried): Default cookie is __Secure-mercure_access_token, dropping to mercure_access_token when kernel.debug. Custom JS, reverse proxies, or hub config referencing the old name must update in lockstep, and local HTTP dev will diverge from production naming.
  • MEDIUM — Silent Hub Fallback (carried): Empty MERCURE_URL reliably selects FrankenPHP's built-in hub, so a typo'd variable in a non-FrankenPHP deployment does not throw. Still requires an explicit startup assertion. Note the interaction with v0.6.1: an empty url also affects the aud claim, compounding the diagnosis difficulty of a single misconfigured variable.
  • MEDIUM — Debug Tooling API Removal (carried): MercureDataCollector::getPublishers() is removed; internal dashboards, test helpers, or profiler extensions calling it break. Grep before upgrade.
  • LOW-MEDIUM — Two Minor Versions Skipped: We are moving v0.4.2 → v0.6.1, absorbing 0.5 and 0.6 deprecation removals at once. Do not attempt a big-bang upgrade.
  • LOW — default_hub Misconfiguration: Now raises a configuration error instead of a TypeError — a DX improvement that shortens incident diagnosis.
  • Security Overhead (net improvement): JWT validation, topic granularity, and private-topic authorization remain the core design decisions. Separate publisher / subscriber keys mean compromised publisher credentials can no longer mint subscriber tokens. The v0.6.1 aud default is narrowly audience-tightening in effect (it binds tokens to the hub URL they are sent to) and does not widen the attack surface.

Key Questions

  1. Scope: Is the deliverable Symfony-only, Laravel-only, or mixed? If Laravel, we consume the standalone client, and the bundle's BC breaks matter only for the Symfony services and for the hub protocol version we pin.
  2. Hub Fleet Inventory: What protocol version is each hub running (dev, staging, prod, any managed hub)? Can we upgrade them before the client, or do we need the protocol_version: 0.x bridge per environment? v0.6.1's aud change only applies to 1.0 hubs — a mixed fleet will behave inconsistently on this claim.
  3. Hub aud Validation (NEW): Does any hub enforce a specific aud value, or use resource_identifier to accept publications at a secondary URL? If resource_identifier was our workaround for a dual-URL setup, v0.6.1 lets us retire it — but only after confirming the hub accepts the new default.
  4. Claim Override Semantics (NEW): If we currently set aud explicitly in jwt.claims, does the v0.6.1 default merge or override? Verify in staging before it reaches a hub with strict audience checks.
  5. JWT Mode: Which hubs use jwt.secret vs. publisher/subscriber keys? Every jwt.secret hub needs iss / sub / client_id defined before the deploy lands.
  6. Legacy API Usage: Any remaining PublisherInterface, TraceablePublisher, StaticJwtProvider, or getPublishers() references? (Grep the full repo, not just src/.)
  7. Runtime Target: Are we on FrankenPHP worker mode (Laravel Octane / Symfony Runtime)? Determines whether the runtime hub factory and the TraceableHub cross-request memory fix are material — and whether the empty-url behaviour is a feature or a trap.
  8. URL Topology: For each environment, does the app reach the hub at the same URL the browser uses, or an internal one? Dual-URL environments are exactly the ones whose publish-auth behaviour changes in v0.6.1 — and exactly the ones where our url/public_url config must be correct.
  9. Cookie Consumers: Any frontend, proxy, or hub-side config hardcoding the old access-token cookie name?
  10. Publish Isolation: Do we want a dedicated http_client for Mercure publishing with a short timeout and its own circuit breaker, so a slow hub cannot exhaust the app's HTTP connection pool?

Integration Approach

Stack Fit

  • Laravel (Primary Target): Use symfony/mercure:^0.9 standalone. Bind HubInterface and RemoteHubInterface as singletons; map config/mercure.php to url, public_url, jwt or publisher/subscriber, http_client, protocol_version. v0.6.1 simplifies this config — no resource_identifier branch needed for dual-URL hubs. No bundle, no autowiring, no profiler.
  • Symfony Services: Full bundle benefit — autowirable RemoteHubInterface (global default plus per-hub named arguments), per-hub http_client, and the runtime Hub / FrankenPhpHub factory.
  • Frontend: Mercure.js (SSE), native HTTP clients (mobile long-polling), or server-side PHP. Cookie-based subscriber auth requires the __Secure- prefixed name in production. Note: the v0.6.1 change is publisher-side only — no frontend impact.
  • Runtime: FrankenPHP worker mode is a first-class citizen; standard PHP-FPM remains fully supported.

Migration Path

Treat this as a three-stage program, not a version bump. v0.6.1 is a patch that folds into stage 3, not a new stage.

  1. Audit (no code changes) — Inventory every hub and its protocol version; record each environment's URL topology (single vs. internal/public split) and any aud / resource_identifier usage; grep for removed APIs (PublisherInterface, TraceablePublisher, StaticJwtProvider, mercure.hub.*.jwt_provider, getPublishers()); grep for the old cookie name across JS, proxy, and hub configs; enumerate environments relying on implicit JWT claims.
  2. Bridge Release (pin protocol_version) — Where hub upgrades lag, set protocol_version: 0.x, and land the jwt.claims additions (iss/sub/client_id) plus the PublisherInterface → HubInterface rename in the same change. The bridge keeps the upgrade reversible without a full rollback. The v0.6.1 aud default does not apply under the 0.x bridge — so bridge environments carry the old audience behaviour and must be revisited at cut-over.
  3. Cut Over — Upgrade hubs to Mercure 1.0, then remove the protocol_version pin to take the 1.0 default. On v0.6.1, this is also when the aud-includes-url default takes effect: retire resource_identifier, verify publication auth on every dual-URL environment, and adopt publisher / subscriber keys plus per-hub http_client where warranted.

Compatibility

  • Protocol: 1.0 default. 0.x hubs require explicit protocol_version: 0.x; both string and ProtocolVersion enum accepted.
  • aud Claim (NEW): Protocol 1.0 publisher tokens now carry the hub url in the default aud. resource_identifier is no longer required for dual-URL hubs. Hubs enforcing a fixed audience must be re-validated. Explicit jwt.claims.aud interaction must be verified in staging — do not assume override.
  • symfony/mercure: ^0.9 permitted — verify the standalone client in Laravel's composer.lock matches what the Symfony services resolve. v0.6.1 is a bundle-side default change, so the two do not need to move in lockstep on this specific item.
  • Cookie Naming: __Secure-mercure_access_token by default; mercure_access_token when kernel.debug is true. A frontend test matrix item.
  • PHP / Symfony: No change to PHP floor implied; Symfony 5.4–7.x remains the tested band. The v0.4.2 Twig registration edge case is unchanged and not a regression risk here.
  • Hub Requirements: JWT validation, HTTP/2 for performance, and CORS for external hubs — unchanged.

Sequencing

  1. Config Hardening First — Add iss / sub / client_id to jwt.claims for every jwt.secret hub, before deploying. This is the only change that can prevent the container from booting; it is independent of code and can be merged early.
  2. API Rename — PublisherInterface → HubInterface, TraceablePublisher → TraceableHub, StaticJwtProvider → StaticTokenProvider; drop any getPublishers() usage.
  3. Pin protocol_version per environment while hubs are upgraded, then remove the pin.
  4. Verify aud Behaviour (v0.6.1) — In staging, with a dual-URL hub and protocol 1.0, confirm: (a) publication succeeds without resource_identifier; (b) any explicit aud config merges as expected; (c) a null url does not emit an empty audience. One environment, one hub, before any shared config lands.
  5. Retire resource_identifier — Only after step 4 passes, and only on environments whose hubs are on 1.0.
  6. Cookie Alignment — Update frontend, proxy, and hub config to the __Secure- name in non-debug environments.
  7. FrankenPHP Guard — Add a boot-time assertion that MERCURE_URL is non-empty unless the runtime is genuinely FrankenPHP; this also protects the aud claim from being built off an unintended URL.
  8. Publish Isolation — Bind a dedicated short-timeout http_client (or the equivalent Guzzle client in Laravel) for the publish path.
  9. Rollout Order — Dev → CI → Staging → Production, one environment at a time, given the boot-time failure mode. v0.6.1 adds no boot-time risk, so it can ride the same window as the v0.6.0 upgrade rather than triggering a separate change.

Operational Impact

Maintenance

  • Bundle Cadence: Follows Symfony's release cycle. v0.6.1 is a one-line behavioural default — low carrying cost, no new API surface, no deprecations. The deprecation cleanup delivered in v0.6.0 remains the one-time maintenance win in this line (removes symfony/mercure 0.5 noise from container boot logs and profiler traces).
  • Config Surface Slightly Reduced: v0.6.1 eliminates a resource_identifier workaround for dual-URL hubs — one fewer per-environment special case to document and drift-check. Counterweight: per-hub http_client, publisher/subscriber keys, and explicit jwt.claims remain, so a documented config contract plus a config-lint step in CI is still worthwhile — now with an added assertion on url correctness, since it feeds the audience claim.
  • Hub Maintenance: Self-hosted hubs still require OS updates, backups, and capacity management; managed hubs shift that burden but add vendor concentration. Protocol 1.0 stability plus a patch-level client fix reduces churn risk on this line item.
  • Topic Hygiene: Unchanged — document naming conventions and retire stale user-scoped topics.

Support

  • First-Line Diagnostic (unchanged, still #1): "Container will not compile" after upgrade is almost always missing iss/sub/client_id in jwt.claims for a jwt.secret hub. Top of the runbook.
  • Second Diagnostic: Unresolvable PublisherInterface → incomplete HubInterface migration. Explicit and greppable.
  • Third Diagnostic (NEW): Publications rejected with 401 on a dual-URL environment, app logs clean, hub logs show an audience/aud mismatch. Causes: hub enforcing a fixed audience that the new default does not satisfy; an explicit jwt.claims.aud colliding with the new default; or url pointing somewhere the hub does not recognise as itself. First check url vs. public_url, then hub-side aud config. Note the inverse of the usual pattern: after v0.6.1 this class of ticket should get rarer, not more common — if it spikes, suspect misconfigured url, not the upgrade.
  • Fourth Diagnostic: Subscriber auth failing in production only → __Secure-mercure_access_token mismatch against the hub's configured cookie. Reproduces inversely in debug (mercure_access_token), so do not trust local success.
  • Fifth Diagnostic: Updates going nowhere on a non-FrankenPHP host → empty MERCURE_URL silently selecting the embedded hub. Fixed by the boot-time assertion above.
  • Debugging Surfaces: Symfony profiler (TraceableHub), hub logs (JWT/audience errors), Mercure.js console (connection/subscription). The TraceableHub memory fix from v0.6.0 means profiler output is trustworthy in Octane/FrankenPHP worker mode.
  • Config Error Quality: default_hub pointing at an undefined hub produces a configuration error rather than a TypeError, shortening triage.

Scaling

  • Horizontal Scaling: App tier stays stateless and scales independently; hub tier scales via load-balanced instances or a managed hub.
  • Publish Latency Isolation: The per-hub http_client remains the most directly useful scaling lever in this release line — a short timeout plus a bounded connection pool on the publish path prevents a degraded hub from consuming app workers' HTTP capacity.
  • Topology Decoupling (v0.6.1): Because the audience claim is derived from url, we can now place the hub behind an internal service address for app traffic while exposing a different public URL to browsers without a bespoke claim workaround per environment. This is directly useful at scale — internal routing, service mesh, and per-region hub endpoints stop leaking into JWT configuration.
  • Credential Topology: Separate publisher and subscriber keys let us scope credentials per service and rotate one without invalidating the other — meaningful blast-radius reduction at multi-service scale.
  • High Volume / High Subscribers: Batch publishes; offload large fan-out to a managed hub or CDN-edge Mercure. JWT generation adds marginal overhead; the v0.6.1 change is constant-cost (one additional claim value) and not a scaling consideration.

Failure Modes

Component Failure Scenario Mitigation
Config (JWT) Container fails to compile — jwt.secret hub missing iss/sub/client_id Pre-merge config audit per environment; CI config lint; rollback gate on boot
Config (Services) PublisherInterface / StaticJwtProvider unresolvable after removal Complete HubInterface / StaticTokenProvider migration in the same change; grep gate
Config (Hub URL) Empty MERCURE_URL silently selects FrankenPHP embedded hub — now also produces a wrong/absent aud Boot-time assertion requiring non-empty URL outside FrankenPHP
Auth (aud) (v0.6.1) Publish returns 401 on a dual-URL hub — audience mismatch after the default aud change, or explicit jwt.claims.aud colliding Staging verification on a dual-URL 1.0 hub before shared config lands; confirm merge-vs-override semantics; keep resource_identifier until proven
Auth (aud) (v0.6.1) aud emitted as null/empty when url is unset, rejected by hub validator Assert non-empty url at boot; inspect a decoded token in staging
Protocol Client on 1.0 default hitting a 0.x hub Pin protocol_version: 0.x as a bridge; upgrade hubs before removing the pin. Note: bridged hubs do not get the aud fix
Auth Cookie __Secure-mercure_access_token mismatch between app, hub, and browser Align cookie name across JS, proxy, hub config; test over HTTPS, not just kernel.debug
Mercure Hub Hub downtime Managed hub with SLA or multi-region / multi-instance hub
Network Firewall or legacy proxy blocks HTTP/2 Documented long-polling fallback
Publish Path Slow hub exhausts app HTTP client Dedicated http_client with short timeout and bounded pool
App Worker App crashes mid-publish Queue publishes; retry with backoff
Client Browser tab closed / dropped SSE Client-side reconnection logic with backoff
Observability TraceableHub message bleed in worker mode (fixed in v0.6.0) Verify profiler accuracy post-deploy

Ramp-Up

  • Developer Onboarding (Laravel) — ~1 day: service provider, config mapping, publish abstraction, publish-path HTTP client. The bundle's config tree is not available, so budget for hand-rolling what the bundle would have given for free. Slightly lower now: no resource_identifier branch for dual-URL hubs.
  • Developer Onboarding (Symfony) — 1–2 hours for basic setup; autowirable RemoteHubInterface and named-argument per-hub injection shorten this relative to prior releases.
  • Upgrade-Specific Ramp — +2–4 hours for the migration itself: claim audit, API renames, protocol pinning, cookie alignment, plus a dual-URL aud verification pass. This is not a drop-in patch; it is a scheduled migration with a rollback plan. v0.6.1 adds roughly 30–60 minutes of verification and no additional code change.
  • Documentation Gaps to Close Internally:
    • A single upgrade checklist covering: jwt.claims (iss/sub/client_id), PublisherInterface → HubInterface, StaticJwtProvider → StaticTokenProvider, getPublishers() removal, protocol_version pinning, cookie rename, and aud/resource_identifier retirement.
    • URL topology per environment: which environments are dual-URL, and what url vs. public_url should be in each — this is now load-bearing for publish auth, not just for embedded-hub selection.
    • FrankenPHP vs. standard runtime behavior, especially the empty-url selection rule.
    • Cookie naming matrix: production (__Secure-) vs. kernel.debug (mercure_access_token).
    • Long-polling fallback for HTTP/2-limited environments (unchanged gap).
  • Training Focus: topic design and granularity, JWT claim modelling (now including audience semantics), publish-path isolation, and client-side reconnection. The one-hour migration briefing before the upgrade window remains the right call — the boot-time failure mode makes "learn it in staging" an expensive strategy.
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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style