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.
Run a relay
Section titled “Run a relay”liebstoeckel relay --port 8080 --tokens <account-token># or set PRESENT_RELAY_TOKENS=tok1,tok2 ; serve behind TLS for public useThen present through it from your machine:
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).
How it splits responsibilities
Section titled “How it splits responsibilities”-
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. -
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.
Untrusted-HTML isolation
Section titled “Untrusted-HTML isolation”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.
Tokens & quotas
Section titled “Tokens & quotas”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).
Code-first presentations your agent can author. One file out, no server.
Comments