Elyra
Elyra The coding agent e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Release notes
Changelog
Elyra
Broadcasting (SSE)

Broadcasting (SSE)

Askr can push live updates to browsers without an external broker (Reverb, Pusher, Redis pub/sub). PHP publishes an event with askr_broadcast(); Rust holds the client connections and fans events out — across all worker processes, via a shared-memory ring.

This ships as Server-Sent Events (SSE), which covers most "live update" needs (notifications, dashboards, progress, Livewire refreshes). For Laravel Echo there is also a Pusher-compatible WebSocket endpoint (--pusher, a drop-in Reverb, with private/presence auth) — see Features.

WebSocket needs HTTP/1.1

The /app/{key} endpoint upgrades over HTTP/1.1 only. HTTP/2 forbids the Connection and Upgrade headers (RFC 9113 §8.2.2), and Askr does not yet implement the h2 replacement for them, extended CONNECT (RFC 8441) — see Askr-49.

This does not affect browsers: Echo and pusher-js use the browser's WebSocket API, which negotiates HTTP/1.1 for this regardless of what the page was loaded over. Broadcasting works in production.

It does affect test clients. Since TLS negotiates h2 by default via ALPN, a handshake check must ask for 1.1 explicitly, or it gets a 404 — which looks exactly like a missing endpoint:

curl -s -o /dev/null -w '%{http_code}\n' --http1.1 \
  -H 'Connection: Upgrade' -H 'Upgrade: websocket' \
  -H 'Sec-WebSocket-Version: 13' -H 'Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==' \
  https://example.com/app/YOUR_APP_KEY          # 101, not 404

Enabling it

askr serve --root ./public --worker-script examples/laravel-worker.php \
  --broadcast

or in askr.toml:

[broadcast]
enabled = true

Publishing (PHP)

askr_broadcast(string $channel, string $payload): bool
// anywhere PHP runs under Askr — a web request, a queue job, the scheduler:
askr_broadcast('orders', json_encode(['id' => 42, 'status' => 'shipped']));

Channels are arbitrary strings (≤ 64 bytes); payloads are ≤ ~3 KB. A publish from any worker or sidecar reaches subscribers held by any worker.

Subscribing (browser)

Askr serves an SSE stream at GET /askr/events?channel=NAME:

const es = new EventSource('/askr/events?channel=orders');
es.onmessage = (e) => {
    const order = JSON.parse(e.data);
    console.log('order update', order);
};

The stream sends data: <payload>\n\n for each event on the channel, plus a : ping comment roughly every 15 s to keep the connection alive. EventSource reconnects automatically.

How it works

  • A fixed-size ring buffer lives in an anonymous shared mmap (created before fork), so publishes from any process are visible to all.
  • Each worker runs a background task that tails the ring every 50 ms and pushes matching events to the SSE connections it holds locally.
  • SSE responses are true streaming bodies — the connection stays open and frames are written as events arrive.

Latency is ~50 ms (the tail interval). A subscriber that falls more than a ring behind drops the oldest events (fine for live updates). Events are not persisted.

Notes

  • SSE is one-way (server → client). For client → server, make a normal request.
  • Behind a proxy, disable response buffering for /askr/events (Askr sets X-Accel-Buffering: no for nginx; other proxies may need their own setting).
  • For guaranteed delivery, cross-host fan-out, or the Pusher protocol, use a dedicated broker. Askr's broadcasting targets the common single-host, best-effort case with zero extra moving parts.