diff --git a/capsite-checklist.md b/capsite-checklist.md index 5cc1c73..45454a8 100644 --- a/capsite-checklist.md +++ b/capsite-checklist.md @@ -23,7 +23,7 @@ narrative. goal 5. - [x] **1c. diigo-dump evolves into a bookmark cache** - ([diigo-cache/README.md](diigo-cache/README.md); design in the + ([diigo-cache/CONTRIBUTING.md](diigo-cache/CONTRIBUTING.md); design in the JSDoc of `src/cache.ts`, `src/index.ts`, `src/pm-save.ts`): - [x] 1c-i. Two independent page caches, private and public (`PagedCache`, `DiigoCache`, `PublicCache`). @@ -113,7 +113,7 @@ narrative. - [ ] **5. Deployed end-to-end** (order matters): - [ ] 5-i. webapp: `cd webapp && npx wrangler deploy` - [ ] 5-ii. diigo-cache: rate-limit `/api` (and `/public/*`) first - (diigo-cache README, "Deployment concern"); deploy; create + (diigo-cache CONTRIBUTING, "Deployment concern"); deploy; create against prod (ids → password manager; no worker secret); run the fill job. - [ ] 5-iii. madmode: deploy with the service binding; the @@ -235,5 +235,5 @@ to every goal in this tree. Oct 6 2026) - [webapp/README.md](webapp/README.md) — HTTP + RPC surfaces of the factory worker; open TODO on the mint-form placement -- [diigo-cache/README.md](diigo-cache/README.md) — the cache worker: +- [diigo-cache/CONTRIBUTING.md](diigo-cache/CONTRIBUTING.md) — the cache worker: create flow, the two DOs, run/deploy details \ No newline at end of file diff --git a/diigo-cache/CONTRIBUTING.md b/diigo-cache/CONTRIBUTING.md index abef183..69dace9 100644 --- a/diigo-cache/CONTRIBUTING.md +++ b/diigo-cache/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# diigo-cache +# Contributing to diigo-cache -Cloudflare Worker cache over [the webapp factory](../webapp/README.md): +diigo-cache is a Cloudflare Worker cache over [the webapp factory](../webapp/README.md): session-cookie reads of diigo bookmarks, cached in Durable-Object SQLite storage. Evolved from the diigo-dump worker (provenance: [mm-diigo `projects/diigo-bak/`](https://github.com/dckc/mm-diigo)); the @@ -36,6 +36,14 @@ goal 2 preamble). form (JSON for `Accept: application/json`). Both say `cache: HIT` or `MISS`; every page read also logs `{event: "cache", kind, page, outcome}`. +- **Filters on the public page** (`src/query.ts`): `days`, `after`, + `before`, `in` (`2026`, `2026-03`, `2026-Q1`, `2026-W41`), + `by=updated|created`, and `q` (words, `"phrases"`, `#tag`, `-` + negation, gmail/Slack/GitHub date operators). UTC, half-open windows. + The same fields work as a dev-side spec (`toQuery({ days: 7 })`). A + filter queries the stored `items` table through `viewer()`, never + diigo: results are as fresh as the cache, and JSON says `complete` + when every diigo page is stored. - **Publish later**: `POST /_publish` (cache id in the body) wires a public companion to a private-only cache. - **The webkey page and Cap'n Web**: `/cache/#` is a skeleton @@ -111,10 +119,24 @@ Tests run the real worker and both DOs in the vitest pool, with `WEBAPP` bound to [a fake webapp](tests/fake-webapp.js) serving diigo-shaped items (`wrangler.tests.jsonc`). -## Run, deploy +## Run -[How to run / connect / deploy is maintained in the capsite checklist](../capsite-checklist.md) -(single source; this README keeps only worker-level facts). +[The README](README.md) gets a cache going in dev. The same create flow +from a terminal: -Status: create, dump, publish, and public pages exercised against live -diigo in dev (2026-10-07); not yet deployed. + wid=$(curl -s -X POST http://localhost:8787/_mint --data-urlencode \ + 'url=https://www.diigo.com/interact_api/' --data-urlencode \ + 'headers={"user-agent":"Mozilla/5.0","x-requested-with":"XMLHttpRequest","cookie":""}' \ + --data-urlencode 'msPerCall=500' -D - -o /dev/null | grep -i x-webapp-id) + curl -s -X POST http://localhost:8787/_wire -H 'content-type: application/json' \ + -d "{\"webappId\":\"\",\"mode\":\"private\"}" + +Check a filled cache against a diigo RSS export: +`node scripts/compare-export.ts `. + +## Deploy + +Not deployed yet; the order matters (goal 5 in +[the capsite checklist](../capsite-checklist.md)): webapp first +(`cd ../webapp && npx wrangler deploy`), then diigo-cache, after +rate-limiting `/api` (above). diff --git a/diigo-cache/README.md b/diigo-cache/README.md new file mode 100644 index 0000000..05da15f --- /dev/null +++ b/diigo-cache/README.md @@ -0,0 +1,37 @@ +# diigo-cache + +Your diigo bookmarks, cached on Cloudflare, with a public page you can +filter by week: `/public/?in=2026-W41`. Your diigo session cookie +is stored in one Durable Object and nowhere else. + +## Getting started + +```sh +pnpm install +pnpm dev # → http://localhost:8787/ +``` + +Then, in the browser: + +1. **mint the diigo session at webapp**: paste the `Cookie` header from + diigo.com (devtools, any logged-in request). +2. **wire** it, mode **public**. Save the cache id it shows (the + password-manager button does this). +3. **open this cache** → **fill**, and watch it walk your bookmarks. +4. Visit `/public/?days=7`. + +## Bookmarks you hold, a page anyone can read + +- **Filter by date and search:** `?days=7`, `?in=2026-W41` (ISO week, + UTC), `?after=2026-01-01&before=2026-02-01`, `?by=created`, and + `?q=` with words, `"phrases"`, `#tags`, `-negation`, and gmail-style + `newer_than:7d`. Add `Accept: application/json` for JSON. +- **Private stays private:** the public cache drops private bookmarks + before storing anything, so it never holds them. +- **Ids are capabilities:** the cache id is a bearer key you keep in + your password manager. It rides URL fragments and POST bodies, never a + path or query string, so it stays out of logs and history. +- **Cheap to read:** pages come from Durable Object SQLite storage, and + a fill job in the background fetches each diigo page once. + +Design, tests, and deploying: [CONTRIBUTING](CONTRIBUTING.md). diff --git a/webapp/README.md b/webapp/README.md index 8d26f56..b75d2ad 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -3,7 +3,7 @@ DO-backed fluent web client factory: site-agnostic. Deploy once; instances are attenuated handles whose **ids are bearer capabilities** — anyone holding the id string can drive the instance; nobody without it can. -TODO: this proxy-page flow (see diigo-cache README) vs running webapp as +TODO: this proxy-page flow (see [diigo-cache CONTRIBUTING](../diigo-cache/CONTRIBUTING.md)) vs running webapp as the primary worker (`wrangler dev -c ../webapp/wrangler.jsonc -c ../diigo-cache/wrangler.jsonc`) so its own port serves the mint form directly; reconsider when the UI grows past a form or two.