WIP: webapp: rotate the diigo cookie in place #1

Draft
dckc wants to merge 8 commits from dckc/webapp-cookie-rotation AGit into main
pull from: dckc/webapp-cookie-rotation
8 Commits
Author SHA1 Message Date
dckc 7876938f3e capsite/docs: describe the wiring diagram for screen readers
The SVG gets role="img" with a <title> and a <desc> that says in prose
who refers to what, since the arrows carry the meaning and are invisible
to assistive tech. The rendered PNG is unchanged.

Generated-by: Claude Opus 5.5
2026-10-09 02:32:22 -05:00
dckc a921d65021 capsite/docs: cookie-rotation wiring diagram (PNG)
A headless-Chromium screenshot of docs/cookie-rotation-wiring.html.

Generated-by: Claude Opus 5.5
2026-10-09 02:32:22 -05:00
dckc 055b572297 capsite/docs: cookie-rotation wiring diagram (source)
Who holds what after mint: Alice holds the store id (rotate, revoke),
diigo-cache and a leaked-id Mallory hold only the instance, whose
init.headerStore reaches the store's read-only facet. Drawn as an SVG
page in the style of MarkM's ERTP slides.

Generated-by: Claude Opus 5.5
2026-10-09 02:32:22 -05:00
dckc 4dd3fe6bde capsite/diigo-cache: the wire page hands the owner the store id
webapp's mint now answers a store id (x-webapp-store-id) beside the
instance id: authority to rotate or revoke the diigo cookie. The wire
page shows it apart from the instance id, with its own copy button,
petname, and password-manager save; the cache never gets it. A webapp
that sends none gets a note on the page and a warning in the log.

Generated-by: Claude Opus 5.5
2026-10-09 02:32:22 -05:00
dckc 3f711a5f4b capsite/webapp: the cookie's store is the owner's, apart from the instance
Letting the instance id rotate the cookie let its holder (the cache, or
whoever learns the id) swap in another account's session, which the
owner's logout would not stop. Now mint makes two things: the instance,
and a header store (lib's MapStoreDo, shaped ("cookie", { keyShape:
"cookie", valueShape: M.string() })). The instance id is authority to
use the cookie; the store id, for the account owner, to rotate (PUT
/store/<storeId>/cookie, or WebAppFactory.store) or revoke (DELETE).
Instances read the store's read-only view at every send, via init's new
headerStore.

This replaces the in-instance cookie store and its migration: instances
minted earlier keep their cookie in init.headers, untouched, and simply
cannot rotate. The MapStoreDo class arrives as wrangler migration v2
(additive).

Generated-by: Claude Opus 5.5
2026-10-09 02:32:22 -05:00
dckc 357e4561c4 capsite/lib: endo's own matcher data, smallcaps, and MapStoreDo
Matchers are now exactly what @endo/patterns makes: tagged records as
from pass-style's makeTagged (M.string() is match:string with payload
[], M.any() is match:any with payload undefined). Structured clone drops
their symbol-keyed and non-enumerable properties, so they cross DO
storage and Workers RPC in marshal's smallcaps encoding (the subset
patterns need).

MapStoreDo is a MapStore as a DO of its own: its creator fixes label and
(encoded) shapes once; its id is full authority, and readOnly() hands
out the queries alone.

Generated-by: Claude Opus 5.5
2026-10-09 02:32:22 -05:00
dckc 10414c2ce6 capsite/webapp: rotate the cookie in place
The cookie moves out of the immutable init into a MapStore (key shape
"cookie", value M.string()) in the instance's DO storage. Handles get
its read-only view via the new WebApp.withHeaderMap, which reads at
every send, so a rotation reaches handles already out and the webapp
id, and with it the cache's wiring, stays put (checklist goal 6).

Whoever holds the id can rotate, by RPC (WebAppFactory.cookies(id)) or
POST /at/<id>/cookie; the diigo account owner revokes by logging out.

Durable state: instances minted earlier keep the cookie in
init.headers. The first handle() moves it to the store and rewrites
init without it, in one transaction. Code from before this commit
would then see no cookie, so rolling back past it needs the reverse
move.

webapp gains a package.json and a vitest workerd pool for the tests.

Generated-by: Claude Opus 5.5
2026-10-09 02:32:22 -05:00
dckc 9179bdbf38 capsite/lib: MapStore and patterns work-alikes
Enough of @agoric/store's MapStore and @endo/patterns' M for the
credential-rotation design: makeMapStore(label, { keyShape, valueShape },
kv) keeps entries in a DO's sync KV under store:<label>:, and readOnly()
hands out the queries alone. Facets are workerd RpcTargets in place of
endo's Far.

Unlike endo, error messages never include keys or values: the values
here are credentials, and messages cross RPC into logs.

Generated-by: Claude Opus 5.5
2026-10-09 02:32:22 -05:00