fix(web): bound retained app documents by size; correct the cookie claim

Copilot review round 5 on #2100.

- The store bounded entry COUNT only, and the route admits 8Mi code units per
  document with a one-hour TTL — ~256Mi retainable on documents a server
  under test chooses the size of. Added a 64Mi retained-size budget evicted
  oldest-first alongside the count bound, with a running total kept in step
  with the map through a single `drop` seam so it cannot drift.
- The isolation claim was wrong about cookies. A separate port makes a
  separate ORIGIN, so DOM access, localStorage, sessionStorage and IndexedDB
  are isolated — but cookies are keyed by host and path, ignoring port, so an
  app served here shares the 127.0.0.1 jar with the Inspector and every other
  loopback service. Not a hole in the Inspector's own auth (the API token is
  a header plus a window global and sessionStorage, none of them cookies) and
  not closable without a distinct host this listener cannot mint, so it is
  stated plainly in both the module header and clients/web/README.md rather
  than left as a claim that is false for one of the three surfaces it names.
- Removed a shell-escaping artifact I left in the migration guide.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-24 01:06:12 -04:00 • committed by GitHub
commit 6011d4de03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -182,6 +182,23 @@ is *for* — a real, allowlistable origin — without minting a port or a DNS na
per app, at the cost of not being a per-app isolation boundary: two such apps
share `localStorage`, `sessionStorage`, and cookies for that origin.
**And the separate port does not isolate cookies from the rest of loopback.**
A distinct port makes a distinct *origin*, so the origin-scoped surfaces —
DOM/scripting access, `localStorage`, `sessionStorage`, IndexedDB — are isolated
from the sandbox proxy and from the Inspector page. **Cookies are not
origin-scoped**: they are keyed by host and path, ignoring port. An app served
here therefore shares the `127.0.0.1` cookie jar with the Inspector on `6274`
and with anything else on loopback — it can read any non-`HttpOnly` cookie set
for that host, and set `Path=/` cookies those services will receive.
This is not a hole in the Inspector's own auth: the API token travels in the
`x-mcp-remote-auth` header and lives in a `window` global and `sessionStorage`,
neither of which another origin can read. Closing the cookie gap properly needs
a distinct *host*, which this listener cannot mint (the Inspector owns no DNS,
and a second loopback address is not portable) — so it is stated rather than
papered over. Don't run the Inspector beside a loopback service whose session
cookie you would not hand to an app under test.
**Every failure falls back rather than blanking the app.** No app-origin
listener, a port that never bound, an older backend with no
`POST /api/app-document` route, a network error — each renders the app the