|
| 1 | +# Spec: the peerd.ai site as a demo / lite app |
| 2 | + |
| 3 | +> Status: **forward-looking spec** (no code here yet). The build lands in the |
| 4 | +> `peerd-site` repo, which vendors snapshots from this one. This file is the |
| 5 | +> reuse plan + portability map, written from the extension side so the seams are |
| 6 | +> documented where the code lives. |
| 7 | +
|
| 8 | +## Goal — two products |
| 9 | + |
| 10 | +1. **Homepage feature demos.** Embedded, interactive widgets that show off the |
| 11 | + sandboxes without installing anything: a JS Notebook you can type into, a |
| 12 | + WebVM terminal, a client-side App. No agent, no key — just the runtimes. |
| 13 | +2. **A "try it out" lite app** (a separate page). Paste a model key (stays |
| 14 | + client-side), and the **real agent loop** drives the **real sandboxes** in |
| 15 | + the page — minus browser control (no tabs/DOM/CDP). The "lite" peerd. |
| 16 | + |
| 17 | +## Why it's feasible — the architecture is already shaped for it |
| 18 | + |
| 19 | +peerd's rule is *functional core, imperative shell — IO is injected*. The agent |
| 20 | +brains and the sandbox runtimes don't depend on the extension chassis; only the |
| 21 | +**hosting** (tabs, the service worker, `chrome.*`) does. Measured couplings |
| 22 | +(`grep` for `chrome.*` / `browser.<api>` / direct polyfill imports): |
| 23 | + |
| 24 | +| Module | Files touching chrome/browser | Verdict | |
| 25 | +|---|---|---| |
| 26 | +| `peerd-provider` (adapters) | **0 / 13** | fully portable — injected `fetch` + a key | |
| 27 | +| `peerd-runtime/loop` (agent loop) | **1 / 16** | portable — IO-injected; `agent-loop.js`/`turn-driver.js` import no chassis | |
| 28 | +| `peerd-engine` (sandboxes) | **5 / 15** | runtimes portable; the registries/tab-trackers are the chassis-coupled part | |
| 29 | +| `notebook-tab` (sealed worker) | **1 / 6** | the worker/seal/std are portable; only the host file relays to the SW | |
| 30 | +| `peerd-egress` (vault, safeFetch, denylist, audit) | crypto portable; storage KV wraps `chrome.storage` | swap the KV backend to IDB | |
| 31 | + |
| 32 | +### Most of the portability is *already proven* by existing tests |
| 33 | +- **Provider**: `tests/peerd-provider/*` run the adapters under bun with an |
| 34 | + injected `fetch` — i.e. already outside any extension. |
| 35 | +- **Sealed worker + realm seal**: `extension/tests/unit/notebook-tab/notebook-seal.test.js` |
| 36 | + spawns the production worker over **plain http** (the CDP harness, no |
| 37 | + extension) and plays the host side of the fetch bridge. The runtime already |
| 38 | + runs standalone. |
| 39 | +- **Registries**: `tests/peerd-engine/*-registry.test.ts` run on an injected |
| 40 | + storage stub. |
| 41 | +- **Vault crypto**: `tests/peerd-egress/vault*` run argon2 + WebCrypto under bun. |
| 42 | +- **WebVM**: per `CLAUDE.md`, the site **already vendors a live VM-demo runtime** — |
| 43 | + CheerpX standalone is a shipped precedent. |
| 44 | + |
| 45 | +So the lite app isn't a port — it's a **re-host**: supply the IO graph the loop |
| 46 | +already expects, minus browser control. |
| 47 | + |
| 48 | +## The host shims the lite app must provide |
| 49 | + |
| 50 | +1. **Storage backend.** `peerd-egress/storage/kv.js` is the seam — it abstracts |
| 51 | + `chrome.storage.local`. On web, back it with the existing `storage/idb.js` |
| 52 | + (IndexedDB). Everything above it (vault, denylist, audit, sessions, |
| 53 | + registries) is unchanged. |
| 54 | +2. **In-page sandbox hosting** (the one genuinely-new piece). Today each sandbox |
| 55 | + runs in an extension *tab*; the registries + tab-trackers manage those tabs. |
| 56 | + On web, host each runtime in an **in-page element** — the Notebook/`js_run` |
| 57 | + worker as a `Worker`, the App as an opaque `<iframe>`, the WebVM as the |
| 58 | + vendored CheerpX page/iframe. The registries' *catalog* logic is reusable; |
| 59 | + only the tab-host layer is replaced with an in-page-host layer. |
| 60 | +3. **Replace the SW message bridges with in-page handlers.** The worker's |
| 61 | + postMessage bridges (`fetch-request`, `opfs-request`, `subagent-request`, |
| 62 | + `display`, `log`) are handled today by `notebook-tab.js` — and `opfs-request` |
| 63 | + is **already handled in-page** (not via the SW). Only `fetch-request` relays |
| 64 | + to the SW (`sw/web-fetch` → `safeFetch`); on web, handle it in the page with a |
| 65 | + direct `safeFetch`/`fetch`. Reuse `buildWorkerSource` (the production worker |
| 66 | + assembly) verbatim — it's `import.meta.url`-relative and host-agnostic by |
| 67 | + design (the offscreen job-runner already reuses it). |
| 68 | +4. **A reduced tool manifest.** The machinery exists: `/tools` presets + |
| 69 | + `session.toolManifest` (`tools/manifests.js`), enforced in `gates.js`. Define |
| 70 | + a `lite` preset = the sandbox/compute/memory/web-read tools, **dropping** |
| 71 | + do/get/check, `page_*`, tabs, CDP — the browser-control surface. No new gating |
| 72 | + code; just a manifest. |
| 73 | +5. **Paste-a-key vault flow.** The vault already stores secrets client-side |
| 74 | + (IDB + passphrase/WebAuthn). The lite app's "plug in a test key" *is* the |
| 75 | + vault's existing key path — store the model key client-side, unlock per |
| 76 | + session. (Demo nuance: a "session-only, never persisted" mode is a one-line |
| 77 | + policy on top.) |
| 78 | + |
| 79 | +## Product 1 — homepage feature demos |
| 80 | + |
| 81 | +Each is one runtime + a thin UI, no agent: |
| 82 | +- **JS Notebook**: a code box → `buildWorkerSource` → in-page `Worker` → |
| 83 | + `output-render.js` renders the result. The fetch bridge is in-page. This is the |
| 84 | + smallest, most self-contained demo and the recommended first build. |
| 85 | +- **WebVM terminal**: the already-vendored CheerpX runtime + the existing |
| 86 | + `vm-tab` terminal UI. |
| 87 | +- **App**: an opaque `<iframe>` running a client-side app the agent could build. |
| 88 | + |
| 89 | +## Product 2 — the "try it out" lite app |
| 90 | + |
| 91 | +Wire `makeTurnDriver` (the agent loop) with the host-supplied IO graph: |
| 92 | +- **Provide** (all portable): `vault`, `sessions`/`sessionState`/`sessionCache`, |
| 93 | + the provider stack (`callModel`, `resolveFailoverChain`, `costOf`, …), |
| 94 | + `memory`, `auditLog`, `safeFetch`, the tool dispatcher with the **lite** |
| 95 | + manifest, `settingsStore`, cost/temporal/system-prompt helpers. |
| 96 | +- **Stub or drop**: `browser`, `originOfTabUrl`, `uiPorts`/`uiConnected` (replace |
| 97 | + with an in-page event sink), `currentAppScope`, the do/get/check runner, and |
| 98 | + the dweb deps (`DWEB_ENABLED=false`, the `filterByDweb*`). |
| 99 | +- The streaming UI is the existing Mithril `sidepanel` components, re-mounted in |
| 100 | + the page (they're projections of state — already host-agnostic per DESIGN-12). |
| 101 | + |
| 102 | +## Open questions / risks |
| 103 | + |
| 104 | +- **Provider CORS.** Browser→API calls need permissive CORS. The extension sends |
| 105 | + `anthropic-dangerous-direct-browser-access`; the same applies on a web origin, |
| 106 | + and OpenRouter allows browser calls. **Validate early with a real key** — this |
| 107 | + is the likeliest surprise. (Can't be tested here without a key.) |
| 108 | +- **CheerpX weight + COOP/COEP.** The WebVM needs cross-origin isolation headers; |
| 109 | + the site already serves the VM demo, so this is solved there — confirm the |
| 110 | + lite-app page inherits the same headers. |
| 111 | +- **Key handling.** "Stays client-side" must be real: no key in any request to |
| 112 | + the site's own origin, only to the model API. A session-only (non-persisted) |
| 113 | + default is the safe demo posture. |
| 114 | +- **What to cut.** Browser control (the whole point of the extension) is absent; |
| 115 | + the lite app should say so plainly so it reads as a *demo*, not a lesser app. |
| 116 | + |
| 117 | +## First steps (in `peerd-site`) |
| 118 | +1. Stand up the **JS Notebook homepage demo** — reuse `buildWorkerSource`, swap |
| 119 | + the fetch bridge to in-page. Smallest end-to-end proof of the re-host pattern. |
| 120 | +2. Add the **IDB-backed KV** shim; bring up the vault + a paste-a-key flow. |
| 121 | +3. Define the **`lite` tool manifest**. |
| 122 | +4. Wire `makeTurnDriver` with the host IO graph on the **"try it out"** page; |
| 123 | + start with one provider + the Notebook/`js_run` tool, then add VM/App. |
0 commit comments