Files
archy/.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/13-CONTEXT.md
T

320 lines
19 KiB
Markdown
Raw Normal View History

2026-08-12 10:55:50 +00:00
# Phase 13: AIUI — Conversational Node Control & Content Surfaces - Context
**Gathered:** 2026-08-03
**Status:** Ready for planning
<domain>
## Phase Boundary
Make the embedded AIUI functional in three directions: (1) **human-language node control**
a typed request in AIUI chat reaches a real node action and returns a real result;
(2) **conversational settings** — system settings reachable by conversation, scoped to what
the user granted; (3) **content surfaces made real** — peer files, music, IndeeHub movies and
owned/paid content rendered live in the design AIUI already has. All of it inside a
**user-granted capability sandbox** that keeps keys, secrets and identity material away from
both the browser and the model.
**Not in scope:** cross-node content distribution with payments (the "archipelago content
source"); wallet spends, seed/key operations, federation trust changes and factory reset as
chat-reachable actions; Nostr integration polish; reviving the dead `ContentPanel.vue`
architecture.
</domain>
<decisions>
## Implementation Decisions
### Where the agent loop lives
- **D-01:** The agent loop (model call → tool call → result → model) runs **node-side in
Rust**. The `archipelago` binary owns the loop, the tool registry, and the model key. AIUI
becomes a thin chat client. Rationale: the key never reaches the browser; tool authorization
sits where session auth already lives; Pine/voice can reuse the same tools later.
**Reversibility:** costly — the RPC surface becomes a contract AIUI, and later the voice
pipeline, are written against; moving the loop browser-side afterwards means re-homing key
handling and re-implementing every tool in TypeScript.
- **D-02:** **One assistant, many front doors.** Extend the existing mesh assistant into a
shared service: one tool registry, one backend selector, one place keys live. Mesh/LoRa,
AIUI chat and (later) Pine voice are callers distinguished by permission scope. Avoids two
divergent security models. Note the existing peer-facing controls — `trusted_only`,
`allowed_contacts`, `denied_askers` — are a per-caller scope mechanism that already exists.
- **D-03:** **Split by nature.** The node-side registry owns everything that reads or changes
the node (system, bitcoin, network, wallet, files, media). The existing `ContextBroker`
keeps only what must run in the browser — `navigate`, `open-app`, `launch-app`, `theme`
and remains the consent surface pushing `permissions:update`. Nothing is discarded; each
side owns what only it can do.
- **D-08:** Chat history lives **node-side in the per-node data dir** (`/var/lib/archipelago`),
inheriting the node's backup, factory-reset and future LUKS story rather than growing a
second sensitive-data location.
### Model backends
- **D-04:** Backend chain is **local Ollama first, with Claude *and* Routstr as fallbacks**.
Node data never leaves the node when a local model is available. The assistant already
reports `ollama_detected` / `claude_available`, so the selection signals exist.
**Routstr (<https://github.com/routstr>) is explicitly in scope at the user's request**
it is an OpenAI-compatible endpoint paid per request in Cashu ecash, with providers, models
and prices discovered over Nostr. All three of those substrates already exist in this
codebase (`core/archipelago/src/streaming/` holds Cashu token handling and the
`list-mints`/`configure-mints` RPCs; Nostr discovery is ADR-003/ADR-006).
- **D-05:** Routstr spending is authorized by a **prepaid budget the user sets**. Inference
spends silently within the allowance, then stops and asks. The ceiling is hard — a
prompt-injected model cannot exceed it.
**Reversibility:** reversible — the ceiling is a config value, not a contract.
- **D-07:** The **local model does get tools**, and every write needs confirmation regardless
of backend. A mis-called tool from a weak local model surfaces as a confirmation prompt the
user rejects, not a wrong action. Consequence: the confirm gate does the safety work, so
**backend choice stays a privacy decision rather than a safety one**.
### Authority and sandboxing
- **D-06:** Tools are a **curated allowlist of hand-written tools** — each with its own
schema, permission category, and destructive/confirm flag. The model never sees the full RPC
surface. No auto-generation from the dispatcher: every capability the chat has must be a
decision someone made, which is the only way the sandbox claim stays true.
**Reversibility:** reversible — adding tools later is additive; the allowlist is the point.
- **D-09:** First-cut authority is **reads within granted categories + app lifecycle
(start/stop/restart) + settings writes**. Explicitly excluded from chat reach: keys, seeds,
wallet spends, federation trust, factory reset. Those stay UI-only.
**Reversibility:** costly — widening later is safe, but any capability shipped and then
withdrawn breaks a behaviour users will have learned.
- **D-10:** **Tool authority never derives from content.** Peer-supplied text (file names,
content descriptions, mesh chat, Nostr posts) enters the context inside explicit
untrusted-content delimiters that mark it as data, not instructions. The tool layer takes
its permissions solely from the user's grants and the confirm gate. An injected "now restart
bitcoin" still has to clear a human confirmation naming the real action. Pattern-stripping
filters were considered and **rejected** as an arms race that reads as a guarantee it isn't.
- **D-11:** Write confirmations render **in neode-ui's trusted chrome, outside the iframe**,
drawn by the host from the node's own description of the pending action — never by AIUI and
never from model-authored text. The iframe cannot spoof, restyle or pre-click it. Uses the
project's mandated Teleport-to-body modal pattern.
**Reversibility:** costly — this is the load-bearing anti-spoofing property; moving the
dialog inside the iframe later would invalidate the threat model, not just the styling.
- **D-16:** All 10 permission categories (`apps`, `system`, `network`, `wallet`, `files`,
`media`, `search`, `ai-local`, `notes`, `bitcoin`) **default closed** on a fresh node.
Nothing is shared with the model until deliberately granted. The assistant looks
unconfigured until the user opens categories — accepted cost.
- **Hard constraint from Phase 10:** the `UNAUTHENTICATED_METHODS` hard-refuse gates and the
loopback/auth boundaries must hold with AIUI on the other side of them. They are not to be
widened to accommodate this phase. See `10-CONTEXT.md` D-01..D-04.
### Content surfaces
- **D-12:** **Feed the existing grids from Archy, replacing the LLM-synth source.** AIUI's
design is kept exactly — `FilmGrid`, `SongGrid`, `NewsGrid`, the detail views — and what
fills them changes: peer files, IndeeHub movies, owned/paid content and node media arrive as
real records instead of being regex-scraped out of model prose.
**Reversibility:** reversible — the grids are prop-driven; the data source behind them is
swappable.
- **D-13:** **Build a real music library** — albums, artists, tracks, tag/metadata extraction,
an index that stays fresh. The user chose this over the narrower MIME-filtered-files option
after being told no library domain exists today. It lands as **its own wave of plans inside
Phase 13, not blocking the rest** — peer files, movies and conversational control ship on
their own track and the library lights up `SongGrid` when ready.
**Reversibility:** one-way — an album/artist/track schema and its on-disk index become a
persisted data model with a migration cost once nodes have indexed libraries; changing the
entity model afterwards needs a reindex path, not just a code change.
- **D-14:** **IndeeHub and peer video are surfaced through the content + paid-unlock
subsystem that already exists** (invoices, `X-Payment-Token`, Range streaming). No new
payment rail. The cross-node "archipelago content source" from the Phase 2 note is deferred
— it is a distribution and payments feature spanning federation, not an AIUI surface.
### Delivery and the two-repo split
- **D-15:** AIUI is **built and shipped with the frontend, versioned and verified** — the
rsync path is kept because it is the one that works, but made deliberate: AIUI's commit
pinned in this repo, `VITE_BASE_PATH=/aiui/` enforced by the build script rather than
remembered, and a post-deploy check that **fetches a live asset** instead of trusting a
directory listing. Making AIUI a signed-catalog app was considered and rejected for this
phase: `*-ui` apps are outside the catalog by design today, and changing that platform rule
mid-phase is its own work.
- **D-17:** AIUI **keeps its standalone mode**; embedded mode delegates to the node. It goes
on working on its own with its own proxy for development and for anyone running it outside a
node; when `embedded=true` it hands the loop, the tools and the key to Archy. The dev loop
stays fast — no node required to work on the UI.
- **D-18:** **Push access to the AIUI repo is confirmed before planning starts**, treated as a
prerequisite rather than discovered mid-plan. Last time this surfaced at execution and left
neode-ui shipping two query params that were inert no-ops against every deployed AIUI build
until a maintainer merged (see `.planning/WINDOWS.md` window 4).
### Claude's Discretion
- What the music library indexes over (own filebrowser `Music` folder, peer audio, or both),
the tag-extraction library, and where the index lives — within D-13's bounds.
- Streaming/token delivery for chat responses; context-window budgeting over node data.
- Which specific tools make the first curated allowlist, within D-09's authority ceiling.
- Routstr provider selection strategy among Nostr-advertised providers.
- Per-category mapping of the 10 permission categories onto individual tools.
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### The existing AIUI bridge (this is NOT greenfield — read before designing anything)
- `neode-ui/src/types/aiui-protocol.ts` — protocol v1.0.0, `aiui:` message prefix, the
request/response contract. Defines `AIContextCategory` (10 categories) and `AIActionType`
(`install-app | open-app | navigate | launch-app | search-web | read-file | tail-logs`).
- `neode-ui/src/stores/aiPermissions.ts` — the 10 user-toggled permission categories with
labels; `isEnabled` / `toggle`.
- `neode-ui/src/services/contextBroker.ts` — the 624-line origin-scoped postMessage broker
that "checks permissions, fetches data from Pinia stores, sanitizes it (strips sensitive
fields), and responds". The asset D-03 splits.
- `neode-ui/src/services/__tests__/contextBroker.test.ts`, `neode-ui/src/views/__tests__/chatAiuiEmbed.test.ts` — existing coverage to keep green.
- `neode-ui/src/views/Chat.vue` — the iframe embed, `aiuiUrl` construction, origin check, the
`ready` handshake, `allow="microphone"`.
### The existing node-side assistant (the thing D-02 extends)
- `core/archipelago/src/api/rpc/mesh/assistant.rs``mesh.assistant-status` /
`mesh.assistant-configure`; reports `ollama_detected`, `claude_available`, `models`,
`trusted_only`, `allowed_contacts`, `denied_askers`; key at `data_dir/secrets/claude-api-key`.
- `core/archipelago/src/mesh/listener/assist.rs``run_assist`, `is_sender_allowed`,
`call_ollama`, `call_claude`, `cap_reply`. **Q&A only — no tool-calling today.**
- `core/archipelago/src/api/rpc/dispatcher.rs` — the method registry (`mesh.assistant-*` at
~445). **Confirmed: there are no `pine.*` methods** — Pine has no RPC surface.
### Routstr (new integration, user-requested)
- <https://github.com/routstr> — org; `routstr-core`, `routstrd`, `routstr-sdk`, `routstr-chat`.
- <https://docs.routstr.com/> — protocol docs.
- `core/archipelago/src/streaming/` — existing Cashu handling (`gate.rs` verifies/receives
tokens, `pricing.rs`, `session.rs`) and the `streaming.list-mints` / `.configure-mints` RPCs.
Note: currently `#![allow(dead_code)]`, "suppress dead_code until callers land".
### Content subsystem (what D-12/D-14 wire the grids to)
- `core/archipelago/src/content_server.rs``ContentItem` shape (`id`, `filename`,
`mime_type`, `size_bytes`, `description`, `access`, `availability`, `added_at`),
`AccessControl` (`Free | PeersOnly | Paid`), `parse_range_header`, the paid-preview logic
and the ISOBMFF faststart check.
- `core/archipelago/src/api/handler/content.rs``GET /content`, `/content/<id>`,
`/preview`, `/invoice`; Range → 206 with `Content-Range`; 402 body with `price_sats`.
- `core/archipelago/src/api/handler/proxy.rs:188-265` — the peer Range-streaming proxy
(`/api/peer-content/<onion>/<id>`). Its docstring explains why base64 blobs broke seeking.
- `core/archipelago/src/api/rpc/content.rs` — the `content.*` RPCs including
`browse-peer`, `download-peer*`, `preview-peer`; auto-filing by MIME at ~668.
- `neode-ui/src/composables/useAudioPlayer.ts`, `neode-ui/src/components/GlobalAudioPlayer.vue`
— the singleton bottom-bar player. **Audio never opens the lightbox** — enforced in 5 places.
- `neode-ui/src/api/filebrowser-client.ts` — the scoped-token pattern (`app.filebrowser-token`)
that D-01 follows. **Known leak to fix rather than propagate:** `streamUrl` puts the JWT in
the URL query string.
### Prior phase context (locked decisions that constrain this phase)
- `.planning/phases/10-key-material-hardening/10-CONTEXT.md` — D-01..D-04, the
`UNAUTHENTICATED_METHODS` hard-refuse gates. **Must not be widened.**
- `.planning/phases/02-ui-performance/02-CONTEXT.md` — D-14 (the shipped AIUI embed defaults)
and the Deferred Ideas block, which is the origin of this phase.
- `.planning/phases/02-ui-performance/02-AIUI-D14.md` — the embed parameter contract, AIUI's
repo location and branch, and the push-access history.
- `.planning/WINDOWS.md` window 4 — the 403 that made D-18 a prerequisite.
### Project invariants
- `CLAUDE.md` — commit/push discipline, rootless-Podman invariant, the frontend-build verify
rule (grep the built bundle), "verify on the real node before any tag".
- `.planning/PROJECT.md` — ADR-003 (Nostr discovery), ADR-006 (DID-signed, trust tiers),
ADR-008 (dual keys from one seed), ADR-009 (container security).
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- **The permission + consent layer already exists** — 10 categories, a store, a broker that
sanitizes, and tests. This phase extends it rather than inventing it.
- **The assistant already abstracts two model backends** and already holds a key server-side
at `data_dir/secrets/claude-api-key` — the pattern D-01 generalizes.
- **Cashu, Nostr and Lightning are all already in-tree**, which is why Routstr is a smaller
lift here than it would be elsewhere.
- **Range-streaming media delivery is solved** — both own files (filebrowser `/api/raw`) and
peer files (the Rust proxy). The grids need data, not a transport.
### Established Patterns
- Audio belongs to the global bottom-bar player, never the lightbox (enforced in 5 call sites).
- Modals Teleport to body for a full-screen backdrop (project rule, repeatedly reinforced).
- Scoped tokens minted by an authenticated RPC, credentials never reaching the browser.
### Integration Points
- `dispatcher.rs` — where new assistant/tool RPCs register.
- `ContextBroker.handleMessage` — where the browser-only action split (D-03) lands.
- `ChatPage.vue``ContentGridView.vue` → the `*Grid` components — the live render tree the
Archy data must reach (**note `ContentPanel.vue` is dead; do not build through it**).
### Landmines found during scouting (verified, not assumed)
- **AIUI's grids are fed by regex-parsing the model's own reply text** (`updatePanelFromText`
`contentExtraction.ts`), resolving IDs against fixture catalogs that are themselves
injected into the system prompt (`useAI.ts:24-34`). The largest data bucket is
LLM-synthesized, not an API awaiting a base URL.
- **Every "real" data path in AIUI is Vite dev middleware** — all six plugins are
`configureServer`/`configurePreviewServer` only, so they are **absent from a static `dist/`
deploy**. On a node, TMDB posters, web search, RSS and filesystem all 404.
- **`vite-fs.ts:7` hardcodes `PROJECTS_ROOT = '/Users/dorian/Projects'`** — broken on any
other machine, including the Linux dev box.
- **`ContentPanel.vue` is dead code**, taking `ArchyAppsGrid` (the Archy bridge grid),
`FavoritesGrid`, `DiscoverPanel`, `RecipeDetail` and `AppDetail` with it. Clicking a recipe
or an app currently does nothing.
- **`ShareModal.vue`'s mime map omits `m4a`/`aac`/`opus`/`wma`** — those share as
`application/octet-stream`, so they never route to the audio player and are auto-filed to
`Documents` instead of `Music`. Relevant to D-13.
</code_context>
<specifics>
## Specific Ideas
- Routstr was named by the user directly, with the repo link, and asked to be planned in as
part of the backend work — not treated as a future option.
- The sandbox framing is the user's own: "we must sandbox and protect the users sensitive
keys, information, etc whatever they allow access to." The last clause is the design brief —
authority is bounded by what the user allows, not by what the model asks for.
- The origin of this phase is the user's Phase 2 wording: AIUI "talks to the node safely when
permissioned, without leaking data, **using the same command surface as Pine** and everything
else enableable in settings." D-02's shared-service shape is that sentence made concrete.
</specifics>
<deferred>
## Deferred Ideas
- **Cross-node "archipelago content source" with payments** — any IndeeHub install plugs into
every node's content, with payments; same for music. A federation distribution + payments
feature; its own phase (deferred at D-14).
- **AIUI Nostr integration polish** — "make the Nostr integration in AIUI more beautiful."
Carried over from Phase 2's deferred list, still not scoped here.
- **Reviving `ContentPanel.vue` and the plugin-renderer path** — considered and rejected for
this phase; the dead-code inventory should be resolved as cleanup, not as architecture.
- **AIUI's dev-only Vite middleware** (`vite-tmdb`, `vite-rss`, `vite-web-search`, `vite-fs`,
`vite-music-search`, `vite-dev-chats`) — needs a production answer eventually; only the parts
D-12 replaces are in scope now.
- **Pine voice reusing the tool registry** — D-02 makes it possible and is the reason for the
shared-service shape, but wiring the voice pipeline to it is not in this phase.
### Reviewed Todos (not folded)
- *Connected-nodes list must scroll at row-matched height* — keyword match only; belongs to
Phase 1 (UIFIX-02, already complete).
- *Fedimint gateway must not install with a pre-set password* — keyword match only; Phase 1
FED-07 territory.
- *Keep FIPS/Tor pills on cloud files and show them on mobile* — keyword match only; Phase 1
UIFIX-01.
</deferred>
---
*Phase: 13-AIUI — Conversational Node Control & Content Surfaces*
*Context gathered: 2026-08-03*