diff --git a/.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/FOLLOW-ON-SCOPE-PROPOSAL.md b/.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/FOLLOW-ON-SCOPE-PROPOSAL.md index b401d902..002df75a 100644 --- a/.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/FOLLOW-ON-SCOPE-PROPOSAL.md +++ b/.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/FOLLOW-ON-SCOPE-PROPOSAL.md @@ -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.