diigo-cache: fill from a webkey page over Cap'n Web

Starting a fill no longer POSTs the cache id to /_fill. Instead:

- the created page links the cache's webkey, /cache/#<cache id>: a
  skeleton page, the same for every key, that reads the key from the
  fragment (never sent to the server or in a Referer)
- its script trades the key in-band for a capability over Cap'n Web at
  /api: CacheApi.cache(key) returns a CacheCap facet (fill, status,
  fingerprint, the public companion's fill) and never the DO's wiring
  methods; malformed and unknown keys get one refusal that never echoes
  the key, and each refusal is logged without it
- then calls startFill(): trade and call pipeline into one round trip
- the worker serves capnweb's browser build itself at /_capnweb.js (a
  wrangler Text rule), so no third-party script sits beside the key

The /_fill route and its forms are gone. Tests run the real capnweb
client against the worker (fetch routed to worker.fetch).

Generated-by: Claude Opus 5.5
This commit is contained in:
dckc committed 2026-10-07 04:32:44 -05:00
1 parent fbe2538345
commit 8c221abe6f
9 files changed
+397 -112

No files matched your search

+17 -9
View File
@@ -33,20 +33,27 @@ goal 2 preamble).
outcome}`.
- **Publish later**: `POST /_publish` (cache id in the body) wires a
public companion to a private-only cache.
- **Fill job**: `POST /_fill` (cache id in the body; `target=private|public`,
`action=start|stop`) walks every diigo page into a cache in the
background — DO alarms, a few pages per step, progress stored in the
DO (`startFill`/`fillStatus`/`stopFill`). The created page has the
buttons; the public page shows progress.
- **The webkey page and Cap'n Web**: `/cache/#<cache id>` is a skeleton
page, the same for every key; its script reads the key from the
fragment and trades it in-band for a capability over
[Cap'n Web](https://github.com/cloudflare/capnweb) at `/api`
(`CacheApi.cache(key)` → a `CacheCap` facet: fill, status, the public
companion; `src/api.ts`). The worker serves capnweb's browser build
itself at `/_capnweb.js`.
- **Fill job**: walks every diigo page into a cache in the background —
DO alarms, a few pages per step, progress stored in the DO
(`startFill`/`fillStatus`/`stopFill`), driven from the webkey page;
the public page shows progress.
- The `DiigoBridge` entrypoint for other workers (its cache-id source is
the goal-2 wiring TODO).
## State surface
Ids are **webkeys** ([the pattern](../docs/webkeys.md)): 256-bit bearer
secrets that never ride URLs — not query strings, not path segments.
Forms carry them in POST bodies; logs and pages show only a fingerprint
(a prefix of the id's one-way hash).
secrets that never ride a query string or path. The webkey page carries
the cache id in the URL fragment (never sent to the server) and passes
it in-band over Cap'n Web; forms carry ids in POST bodies; logs and pages
show only a fingerprint (a prefix of the id's one-way hash).
- **The cache id**: the cache's read capability (bearer; not logged).
Held by the user — saved to the password manager under a petname they
@@ -69,7 +76,8 @@ diigo through *your* cookie session: each request may spend up to
`FETCH_BUDGET` (40) diigo fetches on uncached pages (goal 1d), paced at
`MS_PER_CALL`. Cached pages cost nothing. Before exposing a deployment:
- rate-limit the public routes — on Cloudflare, the
- rate-limit the public routes and `/api` (Cap'n Web pipelining makes
calls cheap to send) — on Cloudflare, the
[Workers rate-limit binding](https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/)
(keyed by client and/or username); on other workerd hosts, a rate
limit in the proxy in front;