diff --git a/capsite-checklist.md b/capsite-checklist.md
index 673e554..edc9f4a 100644
--- a/capsite-checklist.md
+++ b/capsite-checklist.md
@@ -109,10 +109,11 @@ narrative.
weekly page renders against prod.
- [ ] **6. Cookie expiry**: when diigo's session ends, head refreshes
fail; the public page keeps serving what's stored (say so on the
- page, don't fail silently). Rotation is manual: re-mint via
- `/_connect`. Open question: can an existing cache be re-wired to
- a new webapp id (keeping its cache id and public username), or
- does re-mint mean a new cache? Prefer re-wire — durable ids.
+ page, don't fail silently). Rotation is in place, keeping the
+ webapp id (so the cache's wiring and ids hold), by the store id
+ minted alongside it and kept by the account owner: `PUT
+ /store//cookie` (webapp README, "The header store").
+ Still open: a rotate form in diigo-cache's UI.
- [ ] **7. bib sync #195**: repeat the pattern for the next
read-access need (webapp mints for any origin; the cache's
item shape is diigo-specific): [the zotero goal tree](zotero-checklist.md).
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: