docs: document WS broadcast scope gating and Control UI img-src CSP

This commit is contained in:
Vincent Koc
2026-04-21 13:14:15 -07:00
parent 7d7c0b1dfe
commit 32ccf27e60
2 changed files with 23 additions and 0 deletions

View File

@@ -240,6 +240,17 @@ The Gateway treats these as **claims** and enforces server-side allowlists.
- Presence entries include `deviceId`, `roles`, and `scopes` so UIs can show a single row per device
even when it connects as both **operator** and **node**.
## Broadcast event scoping
Server-pushed WebSocket broadcast events are scope-gated so that pairing-scoped or node-only sessions do not passively receive session content.
- **Chat, agent, and tool-result frames** (including streamed `agent` events and tool call results) require at least `operator.read`. Sessions without `operator.read` skip these frames entirely.
- **Plugin-defined `plugin.*` broadcasts** are gated to `operator.write` or `operator.admin`, depending on how the plugin registered them.
- **Status and transport events** (`heartbeat`, `presence`, `tick`, connect/disconnect lifecycle, etc.) remain unrestricted so transport health stays observable to every authenticated session.
- **Unknown broadcast event families** are scope-gated by default (fail-closed) unless a registered handler explicitly relaxes them.
Each client connection keeps its own per-client sequence number so broadcasts preserve monotonic ordering on that socket even when different clients see different scope-filtered subsets of the event stream.
## Common RPC method families
This page is not a generated full dump, but the public WS surface is broader

View File

@@ -278,6 +278,18 @@ Trusted-proxy note:
See [Tailscale](/gateway/tailscale) for HTTPS setup guidance.
## Content Security Policy
The Control UI ships with a tight `img-src` policy: only **same-origin** assets and `data:` URLs are allowed. Remote `http(s)` and protocol-relative image URLs are rejected by the browser and do not issue network fetches.
What this means in practice:
- Avatars and images served under relative paths (for example `/avatars/<id>`) still render.
- Inline `data:image/...` URLs still render (useful for in-protocol payloads).
- Remote avatar URLs emitted by channel metadata are stripped at the Control UI's avatar helpers and replaced with the built-in logo/badge, so a compromised or malicious channel cannot force arbitrary remote image fetches from an operator browser.
You do not need to change anything to get this behavior — it is always on and not configurable.
## Building the UI
The Gateway serves static files from `dist/control-ui`. Build them with: