> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Dentro il driver di broadcast Mercure

> Un'analisi a livello di sorgente del driver di broadcast Mercure aggiunto al framework Laravel: architettura HTTP/SSE, JWT separati per publish e subscribe, autorizzazione dei canali tramite un unico cookie e canali cifrati end-to-end.

<Warning>
  Il `MercureBroadcaster` di cui parla questa pagina è stato aggiunto nella [PR #61474 di laravel/framework](https://github.com/laravel/framework/pull/61474) (mergiata il 10 settembre 2026). Al momento della scrittura non fa ancora parte di una release taggata e non è documentato sul [sito ufficiale](https://laravel.com/docs/broadcasting). Questa pagina è un'anteprima basata sul codice sorgente e sui docblock — consulta il changelog di `laravel/framework` prima di affidartici in produzione.
</Warning>

## Panoramica

Il livello di broadcasting di Laravel ha da tempo offerto [Reverb](/it/broadcasting), Pusher e Ably come driver "WebSocket-native". Ora è stato aggiunto un nuovo valore `mercure` per il `driver` in `config/broadcasting.php`.

[Mercure](https://mercure.rocks) è un protocollo real-time costruito sui Server-Sent Events (SSE) invece che su una connessione WebSocket bidirezionale. Si comporta più come un long-polling HTTP/1.1 o HTTP/2, il che gli conferisce alcune caratteristiche distintive:

* Attraversa in modo trasparente la normale infrastruttura HTTP (reverse proxy, CDN, load balancer).
* I client possono essere implementati senza altro che l'API `EventSource` del browser — nessuna libreria client dedicata richiesta.
* [FrankenPHP](/it/blog/laravel-cloud) include un hub Mercure integrato, quindi non serve alcuna infrastruttura aggiuntiva per provarlo.

```mermaid theme={null}
flowchart LR
    A["Server<br>broadcast(event)"] --> B["MercureBroadcaster"]
    B --> C["Hub Mercure<br>(Hub / FrankenPhpHub)"]
    C -->|"SSE (EventSource)"| D["Browser<br>Laravel Echo"]
    D --> E["Aggiornamento UI<br>in tempo reale"]
```

## Il nuovo valore `driver`

Il commento con l'elenco dei driver supportati in cima a `config/broadcasting.php` ora include `mercure`:

```php theme={null}
// Supported: "reverb", "pusher", "ably", "mercure", "redis", "log", "null"
```

È fornita anche una configurazione di connessione di esempio:

```php theme={null}
'mercure' => [
    'driver' => 'mercure',
    'url' => env('MERCURE_URL'),
    'public_url' => env('MERCURE_PUBLIC_URL'),
    'secret' => env('MERCURE_JWT_SECRET'),
    'encryption_key' => env('MERCURE_ENCRYPTION_KEY'),
    'claims' => [
        'iss' => env('MERCURE_JWT_ISSUER'),
        'client_id' => env('APP_NAME'),
    ],
    'cookie_name' => env('MERCURE_COOKIE_NAME'),
    'subscribe_expiration' => (int) env('MERCURE_SUBSCRIBE_EXPIRATION', 5),
],
```

Se `url` viene omesso, il driver ricade sull'hub Mercure integrato di FrankenPHP (la funzione `mercure_publish()`). Questa decisione avviene in `CreatesMercureDrivers::mercure()`:

```php theme={null}
public function mercure(array $config)
{
    if (empty($config['url'])) {
        return $this->frankenPhpMercure($config);
    }

    // ...
}
```

Quando gestisci un hub esterno, `url` dovrebbe puntare all'API di management usata per la pubblicazione, mentre `public_url` è l'URL a cui si connette il browser. Separare i due permette configurazioni in cui la pubblicazione avviene su una rete interna (ad esempio Docker Compose) mentre solo l'URL pubblico è esposto al browser.

## Token separati per publish e subscribe

Mercure è un protocollo di controllo accessi basato su JWT. Il trait `CreatesMercureDrivers` costruisce **factory di token separate** per la pubblicazione (server → hub) e la sottoscrizione (browser → hub):

* `secret`, `publish_secret` e `subscribe_secret` possono essere configurati in modo indipendente, con fallback su `secret`.
* Allo stesso modo `algorithm`, `publish_algorithm` e `subscribe_algorithm` possono essere impostati per ogni lato (default `HS256`).
* HS256 richiede un secret di almeno 32 byte, HS384 almeno 48 e HS512 almeno 64; in caso contrario viene sollevata una `InvalidArgumentException`.

```php theme={null}
protected function mercureSecret(array $config, string $side)
{
    $secret = ($config[$side.'_secret'] ?? null) ?: ($config['secret'] ?? null);

    // ...

    $minimumLength = ['HS256' => 32, 'HS384' => 48, 'HS512' => 64][$algorithm] ?? 0;

    if (strlen($secret) < $minimumLength) {
        throw new InvalidArgumentException(/* ... */);
    }

    return $secret;
}
```

Il token di pubblicazione porta `Grant::ACTION_PUBLISH` per ogni topic (`*`) ed è memoizzato da `CachingTokenProvider`, così non viene generato un nuovo JWT a ogni singola chiamata di broadcast.

## Autorizzazione dei canali tramite un singolo cookie

La più grande differenza architetturale rispetto ai driver WebSocket è che l'autorizzazione del sottoscrittore avviene tramite **un unico cookie**. `MercureBroadcaster::auth()` accetta un array `channel_names` (limitato a 100 per richiesta), valuta l'autorizzazione per ciascun canale e poi emette un unico cookie di autorizzazione che li copre tutti:

```php theme={null}
public function auth($request)
{
    $channelNames = (array) $request->input('channel_names', []);

    if ($channelNames === [] ||
        count($channelNames) > 100 ||
        $channelNames !== array_filter($channelNames, 'is_string')) {
        throw new AccessDeniedHttpException;
    }

    // Valuta l'autorizzazione per ciascun canale e accumula le grant...

    return (new JsonResponse([/* ... */]))
        ->cookie($this->makeAuthorizationCookie($request, $grants, $user));
}
```

Un dettaglio importante: un singolo canale rifiutato non fa fallire l'intera risposta — viene marcato come `denied: true` nel payload di risposta, mentre gli altri canali autorizzati continuano a funzionare. Questo è cruciale perché Mercure multiplexa molti topic su una sola connessione EventSource, quindi i canali possono essere aggiunti o rimossi a metà sessione senza dover chiudere la connessione.

I canali presence ricevono una struttura `Grant` diversa rispetto ai normali canali `private`/`private-encrypted`, mirata al pattern di URL di sottoscrizione usato dall'[API Subscription](https://mercure.rocks/docs/hub/concepts/active-subscriptions) di Mercure.

## Canali cifrati end-to-end

I canali con prefisso `private-encrypted-` sono trattati come cifrati end-to-end (E2EE) — il che significa che nemmeno l'hub Mercure vede il payload. È una funzionalità che i driver Pusher e Reverb esistenti non offrono.

Impostare una `encryption_key` di 32 byte codificata in base64 attiva il `ChannelEncrypter`, che avvolge il nome dell'evento, il payload e il socket ID in un JWE (JSON Web Encryption) prima che `broadcast()` lo invii. L'hub inoltra sempre e solo byte cifrati.

```php theme={null}
'encryption_key' => env('MERCURE_ENCRYPTION_KEY'),
```

```shell theme={null}
php -r "echo base64_encode(random_bytes(32));"
```

I sottoscrittori decifrano nel browser usando il campo `jwk` (JSON Web Key) restituito dalla risposta di `auth()` — uno scambio di chiavi out-of-band raccomandato dalla specifica Mercure, in modo che l'hub non venga mai a conoscenza della chiave.

<Info>
  I canali presence non possono essere cifrati, perché la loro lista dei membri passa per design attraverso l'API Subscription dell'hub — cosa incompatibile con l'E2EE.
</Info>

## Whisper (messaggi diretti client-to-client)

Quando `client_events` è abilitato (impostazione predefinita), a ogni canale protetto viene assegnato un "topic whisper" dedicato su cui i sottoscrittori possono pubblicare:

```php theme={null}
if ($this->clientEvents && $whisperTopics !== []) {
    $grants[] = new Grant([Grant::ACTION_SUBSCRIBE, Grant::ACTION_PUBLISH], $whisperTopics);
}
```

Il topic vero e proprio del canale rimane server-only, così un client non può mai falsificare un evento originato dal server. Questo rispecchia la funzionalità "client events" di Pusher, ma tiene l'autorizzazione ben separata usando topic distinti.

## Convenzioni di naming dei topic

Poiché gli hub Mercure sono spesso condivisi tra più applicazioni, i topic vengono inseriti in un namespace tramite un `topic_prefix` per evitare collisioni. Il default è:

```php theme={null}
protected string $topicPrefix = 'https://laravel.alt/echo/',
```

`.alt` è un suffisso DNS riservato e non risolvibile ([RFC 9476](https://www.rfc-editor.org/rfc/rfc9476.html)), quindi non può mai collidere con un dominio reale. I nomi dei canali sono URL-encoded secondo l'[RFC 3986](https://www.rfc-editor.org/rfc/rfc3986.html) in un singolo segmento di path:

```php theme={null}
protected function channelTopic($channelName)
{
    return $this->topicPrefix.'channel/'.rawurlencode($channelName);
}
```

## Dominio del cookie e messaggi di errore

Dato che un hub Mercure spesso gira su un sottodominio diverso da quello dell'applicazione (per esempio `mercure.example.com` contro `app.example.com`), un fallimento nella risoluzione di un dominio cookie condiviso solleva un'eccezione descrittiva:

```php theme={null}
throw new BroadcastException(sprintf(
    'Mercure error: %s. Adjust the Mercure "public_url" configuration value so the hub [%s] shares a registrable domain with the application host [%s].',
    rtrim($e->getMessage(), '.'), $this->hub->getPublicUrl(), $request->getHost()
), 0, $e);
```

Se usi un nome di cookie con prefisso `__Secure-` o `__Host-`, il driver verifica anche in fase di boot che `public_url` sia in HTTPS:

```php theme={null}
if (str_starts_with($hub->getCookieName(), '__') &&
    parse_url($hub->getPublicUrl(), PHP_URL_SCHEME) === 'http') {
    throw new InvalidArgumentException(/* ... */);
}
```

## Scegliere tra Reverb, Pusher/Ably e Mercure

| Driver        | Trasporto  | Infrastruttura                             | Cifratura E2E             |
| ------------- | ---------- | ------------------------------------------ | ------------------------- |
| Reverb        | WebSocket  | Richiede server self-hosted                | No                        |
| Pusher / Ably | WebSocket  | SaaS                                       | No                        |
| Mercure       | SSE (HTTP) | Hub self-hosted, o integrato in FrankenPHP | Sì (`private-encrypted-`) |

Mercure è una buona scelta quando non ti serve una connessione bidirezionale persistente — notifiche, aggiornamenti di avanzamento o casi d'uso in stile chat che sono principalmente server-to-client. Se stai già usando FrankenPHP, funziona senza alcuna infrastruttura aggiuntiva.

## Pagine correlate

* [Fondamenti del broadcasting](/it/broadcasting)
* [Laravel Cloud](/it/blog/laravel-cloud) (runtime basato su FrankenPHP)


## Related topics

- [Broadcasting](/it/broadcasting.md)
- [Service provider differiti](/it/advanced/deferred-provider.md)
- [Il nuovo ecosistema di analisi del codice Laravel — surveyor / ranger / roster](/it/blog/laravel-ecosystem-analysis.md)
- [SessionEvent](/it/packages/laravel-copilot-sdk/session-event.md)
- [Illuminate\Support\Manager — anatomia del sistema di driver](/it/advanced/manager.md)
