From 4dd3fe6bdeb4393550df9f7fee80d6f98c51111d Mon Sep 17 00:00:00 2001
From: Dan Connolly
Date: Thu, 8 Oct 2026 15:48:10 -0500
Subject: [PATCH] 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
---
diigo-cache/src/index.ts | 53 +++++++++++++++++++++++++++-------------
1 file changed, 36 insertions(+), 17 deletions(-)
diff --git a/diigo-cache/src/index.ts b/diigo-cache/src/index.ts
index 7c01c0b..8ee5ea2 100644
--- a/diigo-cache/src/index.ts
+++ b/diigo-cache/src/index.ts
@@ -174,11 +174,13 @@ async function proxyMint(request: Request, env: Env): Promise {
const detail = (await res.text()).slice(0, 200);
return home(env, { error: `mint said ${res.status}: ${detail}` }, null);
}
- // webapp answers {id} (+ x-webapp-id); the id rides the header, the
- // wire page is ours
+ // webapp answers {id, storeId} (+ x-webapp-id, x-webapp-store-id);
+ // the ids ride the headers, the wire page is ours
const id = res.headers.get("x-webapp-id");
if (!id) return home(env, { error: "mint produced no id" }, null);
- return await wirePage(id);
+ const storeId = res.headers.get("x-webapp-store-id");
+ if (!storeId) console.warn({ event: "mint", outcome: "no store id: this session cannot be rotated" });
+ return await wirePage(id, storeId);
} catch (err) {
console.error({ event: "mint", error: String(err) });
return home(env, { error: `mint failed: ${String(err)}` }, null);
@@ -188,22 +190,37 @@ async function proxyMint(request: Request, env: Env): Promise {
/** the credential-save helper, once per page (see ./pm-save.ts) */
const PM_SAVE_JS = pmSave.toString();
-/** the suggested petname gets the holder's local hh:mm (the worker only
- * knows UTC), so two saves the same day are told apart at a glance */
-const PETNAME_TIME_JS = `{
- const p = document.getElementById("petname");
+/** the suggested petname (in the input with id `petnameId`) gets the
+ * holder's local hh:mm (the worker only knows UTC), so two saves the same
+ * day are told apart at a glance */
+function petnameTimeJs(petnameId: string): string {
+ return `{
+ const p = document.getElementById(${JSON.stringify(petnameId)});
const d = new Date();
p.value += " " + String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
}`;
-
-/** the onclick that hands pmSave its browser powers */
-function pmSaveClick(value: string): string {
- return `pmSave(this, ${JSON.stringify(value)}, { petname: document.getElementById("petname"), credentials: navigator.credentials, PasswordCredential: window.PasswordCredential, log: console })`;
}
-/** the wire step, with the freshly-minted webapp id pre-filled */
-async function wirePage(webappId: string): Promise {
+/** the onclick that hands pmSave its browser powers, saving under the
+ * petname in the input with id `petnameId` */
+function pmSaveClick(value: string, petnameId: string): string {
+ return `pmSave(this, ${JSON.stringify(value)}, { petname: document.getElementById(${JSON.stringify(petnameId)}), credentials: navigator.credentials, PasswordCredential: window.PasswordCredential, log: console })`;
+}
+
+/** the wire step, with the freshly-minted webapp id pre-filled, and the
+ * store id (rotating the cookie) for the account owner to keep */
+async function wirePage(webappId: string, storeId: string | null): Promise {
const fingerprint = await nick(webappId);
+ const storeSection = storeId
+ ? `
keep: the cookie's store id
+
This one is yours alone, not the cache's: with it you can rotate the
+diigo session (log in again, paste the new cookie) or revoke it. Fingerprint
+${esc(await nick(storeId))}
+
+
+
+`
+ : `
note: webapp sent no store id, so this session's cookie cannot be rotated; when it expires, mint again.
`;
const body = `
diigo cache: step 2 — wire the cache
@@ -212,8 +229,10 @@ async function wirePage(webappId: string): Promise {
(private to you: the password-manager username; never sent to the server)
-
-
+
+
+${storeSection}
+
wire
choose the cache mode, then wire — the next page shows the
cache id (copy that too), after which you land back on home: