docs(13): operator decision — web-search setting derives the AIUI CSP

The toggle must change what is POSSIBLE, not ask the frame to behave. Records
the verified mechanics: CSP is nginx-emitted (static add_header), the setting
lives only in browser localStorage today, and the node's nginx self-heal
reverts hand edits — so the setting moves node-side and the CSP derives from
it, allowlisted rather than wildcard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-06 14:15:00 -04:00
co-authored by Claude Fable 5
parent c3277a8323
commit 31f78bdced
@@ -35,7 +35,41 @@ inside the embed:
- **Content enrichment does not work.** Poster/cover art and web-search lookups are outbound
calls to third parties from the frame.
Two directions, and they are a real decision, not a detail:
### OPERATOR DECISION (2026-08-06): the "web search" setting governs this
The existing AIUI **web search** toggle is the switch. On → outbound (relays, enrichment,
search) is permitted. Off → local-only. And per the operator: *"we must make sure settings are
valid"* — the toggle must **change what is possible**, not merely ask the frame to behave.
That makes the CSP **setting-derived** rather than a static choice between (a) and (b) below.
Mechanics, verified on archi-dev-box:
- The CSP is emitted by **nginx**, not the binary —
`/etc/nginx/sites-enabled/archipelago` `location /aiui/`, one static `add_header
Content-Security-Policy … connect-src $scheme://$host:*/aiui/ blob: data: …`.
- The setting currently lives **only in the browser**: `localStorage['aiui-web-search']`
(`aiui/packages/app/src/stores/chat.ts:131`). A browser-side flag cannot drive a
server-emitted header, and the iframe must not be able to widen its own sandbox.
So the work is:
1. **Move the setting node-side** (same shape as `grants.json`: persisted, default-closed,
owner-owned) with an RPC to read/set it. AIUI reads it rather than owning it.
2. **Derive the CSP from it** — nginx `map`/`include` of a node-written snippet, or serve the
`/aiui/` document from the binary. Note the node's nginx self-heal reverts hand edits to
`/etc/nginx`, so this must be node-managed, not an operator edit.
3. **Allowlist, not wildcard**, when enabled: the node's configured relays (`wss://…`) plus the
named enrichment hosts — never `connect-src *`, or the setting stops meaning anything.
4. Keep the node-brokered paths screened by the existing egress guard; a widened CSP covers the
frame's *direct* calls only.
Security note to carry into the plan: with the toggle on, the frame can talk to allowlisted
third parties directly, so peer-influenced content reaching those calls is a new exfiltration
sink (cf. 13-12's untrusted-content boundary). Default stays OFF and the owner turns it on
knowingly — which is exactly what makes it a valid setting rather than decoration.
The two directions below are retained as the implementation shapes this decision picks between
for the *brokered* half (enrichment/search), not as a live question about relays:
- **(a) Broker it node-side.** The frame keeps its tight CSP; the node gains explicit,
permissioned endpoints (a relay bridge; an enrichment proxy) that AIUI calls at same-origin.