diff --git a/.planning/RESUME-2026-08-06-media-loop.md b/.planning/RESUME-2026-08-06-media-loop.md index 3287b55b..d3adb68b 100644 --- a/.planning/RESUME-2026-08-06-media-loop.md +++ b/.planning/RESUME-2026-08-06-media-loop.md @@ -74,7 +74,30 @@ session; it now answers 200 through the gate. Item 4's private-films path is unb Still worth a human pass: a real NIP-07 **browser extension** login (this proved the transport and the app's acceptance, using the node's key rather than the extension's). -### 2. AI Data Access grants → node-side (unblocks everything AI) +### 2. AI Data Access grants → node-side — ✅ DONE, deployed + verified (`762c72b4`) +Cause confirmed: `localStorage` is per-ORIGIN and a node answers on several (LAN, +Tailscale, `.local`, hostname), so grants made at one address were simply never set +at another. It also made a working content path look broken — every scope silently returns +nothing without a grant, so "ungranted" and "empty library" are indistinguishable. + +`settings/ai_permissions.rs` (session_policy shape: atomic temp+rename, sanitised on read +AND write, **fails closed** on a corrupt file) + `ai.permissions.get/.set`, absent from the +unauthenticated allowlist. Store seeds from localStorage for instant paint, then reconciles; +**migration pushes local grants UP when the node has none**, so upgrading never silently +revokes what someone already granted. Node wins otherwise, so a revocation on one device +takes effect everywhere. Hydration happens ONCE at broker start — the first attempt did it +per-gate and the existing broker tests caught it by failing on consumed mocks. + +Live proof on archi-dev-box: unauthenticated → 401 (dispatched, not "unknown method"); +set `["media","files","BAD ONE","../etc/passwd"]` → stored `["files","media"]` (malformed +dropped); written to `/var/lib/archipelago/settings/ai_permissions.json` owned by +`archipelago`; **survives `systemctl restart archipelago`** — the actual complaint. +Rust 7/7, store 18/18, broker 23/23. + +NOTE: testing left `media` + `files` GRANTED on archi-dev-box. That is the state the +operator needs for content anyway, but it was set by the test, not by them. + +### 2b. (superseded — original note) `aiPermissions.ts` uses `localStorage` (`archipelago-ai-permissions`), which is PER-ORIGIN. A node has many origins, so grants vanish when you switch address/device. Move behind an RPC, localStorage as offline fallback, migrate existing local grants. This is what made a films