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
Owner

The cookie's store belongs to the account owner, apart from the webapp instance; the wire page hands the owner the store id so they can rotate or revoke the session. Adds lib/ (MapStore and patterns work-alikes, MapStoreDo) and a cookie-rotation wiring diagram.

Draft: unit tests pass (lib 11, webapp 6, diigo-cache 110) after rebasing on main; not yet tried end to end in dev.

🤖 Generated with Claude Code

The cookie's store belongs to the account owner, apart from the webapp instance; the wire page hands the owner the store id so they can rotate or revoke the session. Adds lib/ (MapStore and patterns work-alikes, MapStoreDo) and a cookie-rotation wiring diagram. Draft: unit tests pass (lib 11, webapp 6, diigo-cache 110) after rebasing on main; not yet tried end to end in dev. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
dckc added 8 commits 2026-10-09 07:33:42 +00:00
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
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
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
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
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
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
A headless-Chromium screenshot of docs/cookie-rotation-wiring.html.

Generated-by: Claude Opus 5.5
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
You are not authorized to merge this pull request.
This pull request can be merged automatically.
This pull request is marked as a work in progress.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin refs/pull/1/head:dckc/webapp-cookie-rotation
git checkout dckc/webapp-cookie-rotation
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dckc/captoys#1