Cache as filesystem: one place, no OS mount (Decentralized Cache Management)

**Note** from Bead: Decentralized Cache Management · [canonical source](https://redfish.acequia.io/guerin/.agents/4456cd24-010a-41e9-a14c-a2d1086893bd/2026-06-14/notes/03-cache-as-filesystem-single-place.md) · session 2026-06-14 · discussion: Talk: Decentralized Cache Management

*Bead `4456cd24` · 2026-06-14 · Stephen's principle. Terminus of [01-cache-as-state-substrate.md](01-cache-as-state-substrate.md); reframes the loopback of [02-sw-cache-broker-and-loopback-sync.md](02-sw-cache-broker-and-loopback-sync.md).* > Stephen: **"The cache as filesystem removes the messiness of mounting OS filesystems and having data in two places."**

## The claim Use the browser's origin-private store as THE filesystem, rather than mounting a real OS directory into the page (File System Access API) or mirroring disk ↔ browser. The cache is not a copy of a file on disk; it **is** the authoritative store, URI-addressed. One place. "Data in two places" is the cache-invalidation problem in miniature — the canonical hard thing, alongside naming. Most real-world cache pain is *self-inflicted* by keeping a second copy and a reconciler. Making the cache the single local place removes that whole class.

## Which store? Cache Storage ≠ OPFS ≠ IndexedDB (do not conflate) *(Added 2026-06-15 after Stephen flagged a conflation: "are you equating the named caches with OPFS?" No — they are three distinct origin-private APIs, and only one carries the URI-binding property.)* | Store | Keyed by | Value | Role in this substrate | |---|---|---|---| | **Cache Storage** (the named caches) | **`Request` = a URL** | `Response` | The **URI-addressed serving face** — what the SW `match()`es, the "filesystem you read by URL." This is the API that "enforces URI binding" (note 02). Coarse: whole-`Response` writes, no byte-level/partial writes, no real directory tree. | | **OPFS** | **path** (dir/file handles) | bytes (mutable, `createSyncAccessHandle`) | The **backing engine** — byte-level mutable store for the CRDT / JSON-Patch op-log (note 01 Q3), SQLite-wasm, large blobs. Path-keyed, *not* URL-keyed; the SW cannot `match()` it without glue. | | **IndexedDB** | key → object (transactional) | structured records | Backing engine for **indices, metadata, the op-log** where transactions matter. | **The layering this implies (and that the earlier draft hid):** "cache as filesystem" = **Cache Storage as the URI face**, *optionally backed by* OPFS/IndexedDB as a mutable engine that materializes into the Cache Storage face as a read-through view. It is **not** one store, and it is **not** OPFS standing in for the cache. Mild but real tension: OPFS reintroduces a *second* addressing scheme (paths) under a substrate whose premise is "URI is the only address." So OPFS belongs *beneath* the URI face as engine, never *as* the addressed store.

## Why it removes the messiness - **Single source of truth, no second writer.** The File System Access route hands you a real OS path that *other native apps mutate behind your back*, plus handles, permission prompts, and handle revocation. OPFS/Cache Storage is origin-private: no mount, no foreign writer, no two-copy reconciler. - **Durability without exposing the OS path tree.** The browser store gives persistence while staying sandboxed: Cache Storage holds the URI-addressed face, OPFS/IndexedDB the mutable engine beneath. You delegate the messy physical-FS management to the browser; you never manage real OS paths. - **Naming is solved.** The key *is* the URI (the cache enforces URI binding, per note 02). No path-vs-URL impedance mismatch.

## Three consequences 1. **Natural terminus of note 01.** "The cache *is* the state" → "the cache *is* the filesystem." The same inversion applied to storage, not just shared values. URI = the only address; cache = the only store. 2. **Dissolves the local leg of coherence.** Invalidation survives only where it is irreducible — at the **peer** boundary (replica ↔ replica). The local OS-FS-vs-browser-copy axis, where most cache pain lives, ceases to exist. What remains is only the distributed-systems problem you cannot avoid (notes 00/01), none of the self-inflicted one. 3. **Fixes the off-namespace problem by construction.** Anything on the OS filesystem is *outside* the URI namespace and invisible to peers precisely *because* it is a mounted file, not a URI — `~/.claude` memory is the standing example (distributed-origin-architecture). Cache-as-filesystem keeps the store inside the namespace by default; nothing leaks to a second, unaddressable place.

## The cost, and the loopback's changed role The browser store (Cache Storage / OPFS / IndexedDB alike) is **opaque to native tooling** — no Finder, no `cat`, no other app reading it. That is the price of not mounting the OS FS. So the note-02 loopback flips role: - It is **no longer a permanent mount**; it becomes an **opt-in projection** — export a file view onto the OS FS only when a native app genuinely needs one, as a deliberate, revocable **saca**. - You pay the "two places" tax only in that scoped moment, instead of always. Mounting the OS FS becomes the *exception you request*, not the *substrate you live on*.

## The sharp local example This bead's own split — local origin `c:/Users/steph/Documents/sites/…` reconciled to the canonical URI by `/bead-sync` — **is** "data in two places." Cache-as-filesystem is the end-state where that split collapses: CE / CB / loopback all read and write the one URI-addressed store, and "sync" becomes **peer-replication, not disk-mirror reconciliation**. (Today's local-origin + `/bead-sync` is the transitional form; this note names what it is transitioning toward.)

## Open threads - **Migration path** — how does today's CE-writes-local-disk + `/bead-sync` flow move toward CE-writes-the-URI-cache? Likely the loopback (note 02) serving the cache *as* the local origin during transition. - **Backup/durability of a single place** — one local place means peer-replication is the *only* durability story; quantify the replication factor / TTL before trusting the browser store (Cache Storage + OPFS/IndexedDB) as sole store. Note also the browser may evict origin storage under pressure unless `navigator.storage.persist()` is granted — a real durability caveat for a sole store. - **Tooling ergonomics** — agents and humans used to `ls`/editors need a projection or a WebDAV face over the cache; the opt-in export must be cheap.