fepli
DevelopersSelf-hostingEnterprise

Live updates

With live updates, open pages show what changed the moment it changes: the free places on the website's offer list, and the places, waiting lists and attendances in the admin. fepli tells the browsers through a Mercure hub that runs inside the web container. Without it, the pages work as before and ask for the numbers when they load.

What changes

  • The website's offer list. The free places and the „Sofort buchbar“ label change in the open page as families apply, withdraw or get a place. Everybody sees them: they are what the list shows anyway.
  • The admin. The places in an offer's head, in the offer list and among its facts, and the attendances in an edition's head, renew when an attendance changes them. Only accounts that may see an offer hear of it: admins of every offer of their tenant, hosts of their own offers and of the offers shared with them.

What the hub hands on is ids and numbers, never names, addresses or text. A browser that hears of a change in the admin asks fepli for the new numbers, and fepli checks that request as it checks any page.

How it works

  • The hub is part of the image. The web server of the image, FrankenPHP, has a Mercure hub built in. You switch it on with a block in its configuration and two keys. A browser that keeps its connection to the hub open holds no PHP process: the hub is a separate part of the server.
  • One connection per browser. The tabs of a browser share one connection to the hub, however many are open.
  • After the response. fepli publishes after it has answered the request that changed something, and after it has cleared the cache for it. Nobody waits for the hub, and a browser that asks for the new numbers gets them.
  • Nothing breaks without it. A hub that is down costs a warning in the log, A live update was not published, and nothing else: the request and the job that changed something go through. The browsers reconnect by themselves.

Switch it on

1. Generate two keys

openssl rand -base64 48

Run it twice: one key for publishing, one for the tokens that let an admin's browser hear the admin's changes. Add both to fepli.env, which the web, the worker and the cron read:

fepli.env

MERCURE_PUBLISHER_JWT_KEY=…
MERCURE_SUBSCRIBER_JWT_KEY=…

2. Switch on the hub in the web container

The hub's configuration is several lines, which an env file can't hold. Give it to the web service in compose.yaml:

compose.yaml

  web:
    <<: *fepli
    environment:
      CADDY_SERVER_EXTRA_DIRECTIVES: |
        mercure {
          transport local
          publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY}
          subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY}
          cookie_name fepli_live
          anonymous
          heartbeat 30s
          write_timeout 600s
        }
  • anonymous lets every browser hear the website's free places. The admin's changes reach only browsers with a token: the admin sets one as a cookie named in cookie_name, for 15 minutes, and renews it.
  • transport local keeps nothing: a browser that lost its connection asks for the numbers once when it is back.
  • heartbeat keeps an idle connection alive through proxies; write_timeout renews every connection after ten minutes.

The hub answers at /.well-known/mercure on every domain of the installation. fepli names it in the pages' <meta name="fepli-live-hub"> once MERCURE_PUBLISHER_JWT_KEY is set.

3. Tell the worker and the cron where the hub is

The worker and the cron publish too, for the changes their jobs make: a lottery, a waitlist that moves up. They run no hub of their own, so they send their updates to the web container. Add its address to fepli.env; the web container publishes to its own hub and ignores it:

fepli.env

MERCURE_URL=http://web/.well-known/mercure

4. Let the stream through your proxy

The connection to the hub stays open and receives an event now and then. Nothing between the browser and the web container may buffer it or close it while it is quiet.

  • Varnish. The Varnish image passes /.well-known/mercure straight through to the web container, without caching it or touching its cookies. Use the current image.
  • Caddy as in the Compose setup passes the stream on as it is.
  • Other proxies need buffering off for /.well-known/mercure and an idle timeout longer than the heartbeat of 30 seconds. With nginx: proxy_buffering off; and proxy_read_timeout 1h; for that location.

Restart the containers: docker compose up -d.

Variables

  • Name
    MERCURE_PUBLISHER_JWT_KEY
    Type
    string
    Description

    The key fepli signs its updates with, and the hub checks them with. Empty switches live updates off.

  • Name
    MERCURE_SUBSCRIBER_JWT_KEY
    Type
    string
    Description

    The key of the tokens an admin's browser shows the hub. Empty switches the admin's live updates off; the website's stay on.

  • Name
    MERCURE_URL
    Type
    URL
    Description

    Where the worker and the cron send their updates, inside the network: http://web/.well-known/mercure. The web container ignores it.

  • Name
    MERCURE_PUBLIC_URL
    Type
    URL
    Description

    Where browsers connect to the hub. Default: /.well-known/mercure on the domain of the page.

  • Name
    MERCURE_COOKIE_NAME
    Type
    string
    Description

    The cookie of the admin's token. Default: fepli_live, the hub's cookie_name above. Change both together, or neither.

Check it

  • The page names the hub. The source of a page of the website has <meta name="fepli-live-hub" …>. An admin page has it with data-subscribe as well.

  • The hub answers. This request stays open and prints : every 30 seconds:

    curl -N 'https://example.fepli.eu/.well-known/mercure?topic=/fepli/editions/<edition>/places'
    

    <edition> is the edition's UUID in its short base58 form, as in the address of the offer list's live.json (in the browser's network tab). On a multi-tenant installation, fepli is the tenant's slug. Change an offer's maximum number of participants in the admin, and the request prints an event: places.

  • One connection per browser. In the browser's network tab: one request to /.well-known/mercure, however many tabs of the installation are open. In the admin, a request to /admin/_live/token comes before it.

Limits

  • One web container. The hub keeps its connections in the web container it runs in. With more than one web container, an update reaches only the browsers connected to the container that published it. Don't switch live updates on for such an installation yet.
  • Updates. Restarting the web container ends every connection. The browsers connect again within seconds and ask for the numbers once.
  • Rights that change. A token holds for 15 minutes, and the hub checks it when a browser connects. An account whose access to an offer is taken away hears of its changes until its browser connects again, at most a quarter of an hour. What reaches it is the offer's id.

Switch it off

Empty MERCURE_PUBLISHER_JWT_KEY and remove CADDY_SERVER_EXTRA_DIRECTIVES, then restart the containers. Pages that Varnish still holds keep naming the hub until they are rendered again; their browsers find no hub and ask for the numbers as before.

Was this page helpful?