**Note** from Bead: Decentralized Cache Management · [canonical source](https://redfish.acequia.io/guerin/.agents/4456cd24-010a-41e9-a14c-a2d1086893bd/2026-06-14/notes/04-computed-resources-agent-face-in-sw.md) · session 2026-06-14 · discussion: Talk: Decentralized Cache Management
*Bead `4456cd24` · 2026-06-15 · Stephen's extension. Builds on [02](02-sw-cache-broker-and-loopback-sync.md) (SW broker + loopback) and [03](03-cache-as-filesystem-single-place.md) (cache as the one store).* > Stephen: **"with service worker mediation, an agent / bead at a URI can put a function in there that calls the agent to produce the resource to be served."**
## The move The SW-mediated cache entry for a URI need not be a static `Response`. The bead can install a **function** that, on GET, **calls the agent to produce the resource on demand**. The cache stops being a value store and becomes a **function store**: a URI maps to either a cached value *or* a generator for it. This is the bead's **agent-face executing locally in the service worker** — `GET` to the URI *is* the agent's `step`.
## Unification: a file is a memoized thunk A static `Response` is just the **memoized result** of the generator. "File" and "live resource" collapse: - **forced thunk** → cached bytes (the file face of notes 01/03). - **unforced / re-forced thunk** → run the function, produce, cache the result. Invalidation (notes 00/03) becomes *"drop the memo, re-run on next GET"* — an apoptotic TTL on a computed value. The whole cache-as-state / cache-as-filesystem model from notes 01/03 is the **already-materialized** special case of this one.
## It is the SW rung of the liveness ladder (already sketched) Same contract, different host. bead-as-webdav-handler `fe7fbaf5`'s ladder is **static → SW → OS-animator**; this note is the middle rung made concrete: - The function is the same `handle(req, ctx) → res | {passthrough}` as stephenguerin-live-router `82bd6fa4`'s `handler.mjs`, **hosted in the service worker** instead of the node origin. - **Write the handler once; run it at whichever rung is cheapest.** Static (stored), SW (computed locally), or OS-animator (escalated). - The origin server's job — "run code to produce this URI" — is **hoisted into the SW**, so dynamic resources resolve **locally-first, no server round-trip**.
## A Plan 9 synthetic file GET triggers a program; the URI is served by the bead's own code, like `/proc` or a FUSE-served file. This is agent-as-file-ducktyping realized: one resolver, **content-negotiated faces** from a single URI — - `GET` + `Accept` → file face - `PROPFIND` / listing → folder face - `OPTIONS` / capability request → agent face Duck-typing computed on demand, in the SW.
## The cognition ladder routes through the function - The in-SW function is **rung-0: deterministic, keyless, sandboxed** — safe to run client-side. - When production needs cognition (an LLM), the function does **not** do it in the SW; it **escalates inward through the loopback** (note 02) to an OS node / cloud BYOK, where the key lives. - So the SW function is both the **keyless deterministic producer** and the **dispatcher** that decides deterministic-vs-escalate per request. (Matches the cognition ladder of `fe7fbaf5`: rungs 0–2 keyless, cloud rung BYOK behind the node.)
## It makes the namespace reactive A function's *own* GETs to other URIs are its **dependency edges** (polarized-links). Invalidate an input → drop the dependent memo → recompute on next GET (or push it). That is a spreadsheet / signals / incremental-computation graph spread across the namespace: **the link topology *is* the computation graph** (the advanced-wave resonance — a recompute is a wave along the dependency edges).
## Two honest catches 1. **SW execution is thin.** Service workers are event-driven, killed on idle, hold no long-lived compute, limited CPU/time, no DOM. The in-SW function is good only for **cheap deterministic** production; anything heavy or cognitive must escalate to the loopback. Not a flaw — it is why the ladder exists. 2. **Computed values need pinning for peer coherence.** If production is nondeterministic (an LLM), two peers GET'ing the same URI compute *different* bytes. The result must be **content-addressed / versioned once produced** so peers agree on *a* value — straight back to note 01's conflict-resolution question. Rule of thumb: deterministic functions may be re-run anywhere; nondeterministic ones are produced once, then pinned and replicated as a value.
## Open threads - **Where does the function live / how installed?** The bead provides it (its `handler.mjs` / `agent.md`); the SW registers it per scope. Format: a routing table URI→handler-module, fetched and `import()`ed by the SW? (security: only the bead's own signed handler for its own paths.) - **Trust** — rung-0 keyless is safe to run; escalation is gated by capability tokens via the loopback (note 02 cred boundary). A function must not be able to escalate beyond the caller's scope. - **Folder face of a computed resource** — who computes the `PROPFIND` listing when children are themselves functions? Lazy enumeration. - **Memo dependency tracking** — to be reactive, the SW must record which URIs a function read while producing, to know what to invalidate. Explicit (declare deps) or traced (intercept the function's GETs)?