Skip to content

The relay

present-relay is a small Bun server that gives a live session public reach beyond the LAN. Its design centers on one rule: the relay never executes a deck’s code.

Terminal window
liebstoeckel relay --port 8080 --tokens <account-token>
# or set PRESENT_RELAY_TOKENS=tok1,tok2 ; serve behind TLS for public use

Then present through it from your machine:

Terminal window
liebstoeckel live my-talk --relay http://localhost:8080 --relay-token <account-token>

This uploads the built deck, gets back public presenter/viewer links + QR, and runs the deck’s server plugins locally (as a privileged peer of the relay).

  1. The relay hosts each session’s Yjs Hub (authoritative, in-memory, TTL-expired) and serves the uploaded deck HTML. Viewers/presenters connect to its WebSocket directly.

  2. Your local machine rehydrates and runs any server plugins, connected to the relay as a privileged “runner” peer, applying their effects to the shared doc. The relay only moves bytes; it never runs deck code.

The relay serves the deck on its own origin, but with a response header:

Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline';
connect-src <relay-ws> <relay-http>; frame-ancestors 'none';
sandbox allow-scripts allow-popups;

Omitting allow-same-origin puts the deck in a unique opaque origin: it can run scripts and sync over WebSocket, but it cannot reach the relay’s cookies, API, or storage, and each context gets a fresh opaque origin (so decks are isolated from one another too). allow-popups is kept only for the presenter pop-out; default-src 'none' blocks any external fetch, since a built deck inlines all its assets. No separate origin or wildcard certificate needed.

Three token layers keep roles separated:

  • account token: your deck-runner authenticating to the relay.
  • session tokens: presenter / viewer roles embedded in public URLs.
  • runner token: the privileged server-plugin peer (WebSocket-only; can’t load the page).

The relay enforces per-account quotas (max concurrent sessions, max deck bytes, session TTL, inbound frame caps) and keeps docs in memory only (ephemeral, for privacy).

Comments

liebstoeckel

Code-first presentations your agent can author. One file out, no server.

© 2026 Leon Kaucher