openvibers/openvibe.bot Commits
Document the two-sided framing contract for the embed panel
Changes
@@@ -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@@@ -65,7 +65,7 @@ Set `BOT_PAIRING_AUTHORITY` to `bot` (the default) or `network`; any other valueSet `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.@@@ -377,7 +377,10 @@ and a "Sign in to control" link that opens `/panel/:id` in a new tab; otherwiseAn 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.
@@ -91,7 +91,8 @@ OV_OAUTH_CLIENT_SECRET=
@@ -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
# 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.
# 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
# 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
#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.
# The anonymous read-only /watch socket behind the embed: open sockets per client address and watchers per robot.
#BOT_WATCH_MAX_PER_IP=20
#BOT_WATCH_MAX_PER_IP=20
@@ -65,7 +65,7 @@ Set `BOT_PAIRING_AUTHORITY` to `bot` (the default) or `network`; any other value
@@ -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.
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.
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.
@@ -377,7 +377,10 @@ and a "Sign in to control" link that opens `/panel/:id` in a new tab; otherwise
@@ -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.
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>;
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
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.
The watcher's panel joins on `wss://openvibe.bot/watch`: no credential, never a cookie or token.