openvibers/openvibe.bot Commits

Document the two-sided framing contract for the embed panel

OP OpenVibers committed 4 Oct 2026 · 08a686634d · 3 files, +7 −3

Asked: Follow-up of https://github.com/OpenVibers/OpenVibe.Live/pull/23 (Render the Bot robot panel iframe on bound channel pages): its review asked for a change in OpenVibe.Bot that the PR cannot make from OpenVibe.Live. The review said: VERDICT: FIX
Harness job bot-follow-up-of-https-github-com-openvi-213109.

Changes

ddiff --git a/.env.example b/.env.example
iindex fe7ecfe..9bddf73 100644
---- a/.env.example
++++ b/.env.example
@@@ -91,7 +91,8 @@ OV_OAUTH_CLIENT_SECRET=
# Pages that may frame a robot's read-only panel (CSP frame-ancestors), comma or space separated. Each entry is a
# bare https origin (no path, query or wildcard); http://localhost:<port> is accepted outside production only.
# An invalid entry stops the service at boot. Default: OpenVibe.Live. A robot is embeddable only after its owner
--# turns on "embed_public" on its panel.
++# turns on "embed_public" on its panel. The embedding page must also allow Bot's origin (BASE_URL, https://openvibe.bot)
++# in its own CSP frame-src, or the browser refuses the frame.
#BOT_EMBED_ORIGINS=https://openvibe.live,https://www.openvibe.live
# The anonymous read-only /watch socket behind the embed: open sockets per client address and watchers per robot.
#BOT_WATCH_MAX_PER_IP=20
ddiff --git a/README.md b/README.md
iindex 876575f..648f40f 100644
---- a/README.md
++++ b/README.md
@@@ -65,7 +65,7 @@ Set `BOT_PAIRING_AUTHORITY` to `bot` (the default) or `network`; any other value
Set `BOT_EMBED_ORIGINS` (comma or space separated; default `https://openvibe.live,https://www.openvibe.live`) to the pages that may frame a robot's read-only panel (the CSP `frame-ancestors` list). Each entry is a bare https origin, with no path, query or wildcard (`http://localhost:<port>` only outside production); an invalid entry refuses to boot. A robot is embeddable only after its owner turns on `embed_public` with the form on its panel (`POST /robots/:id/embed`); the flag is web-only and never appears in `/api/v1`. Migration `0005_embed_public.sql` adds the column, off for every robot.
--The embeddable panel is `GET /panel/:id/embed` (framed only by `BOT_EMBED_ORIGINS`; no cookie needed to watch) and its read-only socket is `wss://openvibe.bot/watch` (docs/protocol.md §2.1); nginx must upgrade `/watch` like `/control`. Set `BOT_WATCH_MAX_PER_IP` (default 20) and `BOT_WATCH_MAX_PER_ROBOT` (default 500) to cap open `/watch` sockets per client address and watchers per robot; over a cap the socket closes 4003.
++The embeddable panel is `GET /panel/:id/embed` (framed only by `BOT_EMBED_ORIGINS`; no cookie needed to watch) and its read-only socket is `wss://openvibe.bot/watch` (docs/protocol.md §2.1); nginx must upgrade `/watch` like `/control`. Framing takes both sides: the embedding page must also list Bot's origin — its `BASE_URL`, `https://openvibe.bot` in production — in its own CSP `frame-src`, computed from that page's own configuration (OpenVibe.Live's `LIVE_BOT_URL`, default `https://openvibe.bot`), or the browser refuses the frame even though `BOT_EMBED_ORIGINS` allows the framer. Set `BOT_WATCH_MAX_PER_IP` (default 20) and `BOT_WATCH_MAX_PER_ROBOT` (default 500) to cap open `/watch` sockets per client address and watchers per robot; over a cap the socket closes 4003.
Set `BOT_WHIP_BASE` in `/etc/openvibe/bot.env` to the WHIP ingest base each device publishes to (`whip_url = <base>/<publish key>`, sent once with the pairing; OpenRe's is `https://ingest.openre.stream/whip`); leave it unset to pair devices without video.
ddiff --git a/docs/protocol.md b/docs/protocol.md
iindex 34c43e7..b869fed 100644
---- a/docs/protocol.md
++++ b/docs/protocol.md
@@@ -377,7 +377,10 @@ and a "Sign in to control" link that opens `/panel/:id` in a new tab; otherwise
An unknown robot is `404`. The e-stop state is always shown; the E-stop button only to an owner or operator.
Its response carries `Content-Security-Policy: default-src 'self'; frame-ancestors 'self' <BOT_EMBED_ORIGINS>;
object-src 'none'; base-uri 'self'` and `Cache-Control: no-store`; every other page keeps
--`frame-ancestors 'self'`.
++`frame-ancestors 'self'`. Framing takes both sides and the two lists are independent: `BOT_EMBED_ORIGINS` lets
++a page frame Bot, while that page must list Bot's origin — its own `BASE_URL`, `https://openvibe.bot` in
++production — in its own CSP `frame-src` (OpenVibe.Live computes it from `LIVE_BOT_URL`, default
++`https://openvibe.bot`). Allow the framer here but not Bot there and the browser refuses the frame.
The watcher's panel joins on `wss://openvibe.bot/watch`: no credential, never a cookie or token.