Per-page dispatchers batching to a shared hub: the tier above the SW (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/10-per-page-dispatcher-batches-to-hub.md) · session 2026-06-14 · discussion: Talk: Decentralized Cache Management

*Bead `4456cd24` · 2026-06-15 · Stephen's refinement of [09](09-write-back-hierarchy-local-pubsub-batched.md). Answers note 09's cross-tab open thread; surfaces a tier note 09 collapsed.* > Stephen: **"or maybe each page has a pub/sub dispatcher and batches to the SW?"**

## The tier note 09 collapsed Note 09 put the **SW heap** as the L1 pub/sub dispatcher. But there is a faster tier above it: the **page's own dispatcher**. Two facts force it: 1. **`postMessage` to the SW is not free.** Page↔SW is cross-thread (cross-process in some browsers), structured-clone serialized, and async — sub-ms, but a real boundary and a real coalescing point. 2. **UI reactivity must be synchronous and in-thread.** Signals/observables fire at ~ns on the main thread; you cannot put every component's read/publish behind an async hop to the SW. So **each page is forced to have its own local dispatcher** regardless — and the right move is to batch *up* to the SW, exactly note 09's write-combining logic applied one tier higher.

## The refined hierarchy | Tier | Speed | Role | Volatility | |---|---|---|---| | **Page dispatcher** (per tab) | ~ns (sync, same thread) | UI reactive bus; **optimistic local view** | dies on navigation — most volatile | | *page → hub* | `postMessage` sub-ms (structured clone) | **coalesce up** | — | | **Shared hub** (SW / SharedWorker) | ~µs–ms | **cross-tab aggregation + durability/sync gateway** | outlives any one tab | | **OPFS → Cache → peers** | note 05 | engine → URI face → replication | durable / remote | So note 09's "SW heap = L1" becomes: **page heap = L1 (per tab), SW = L2 (shared)**.

## The role split (the clarifying part) - **Page dispatcher** = per-context reactive L1. Synchronous, ns, serves the page's own subscribers; volatile per-navigation; the optimistic local view. This is **Firebase's in-page SDK cache**. - **Shared hub (SW)** = cross-tab aggregator + durability/sync coordinator. Receives batched deltas from N pages, merges them, fans out to *other* tabs, and runs the write-back to OPFS/Cache/peers (note 09). This is **Firebase's sync engine**. Batching is **bidirectional across the `postMessage` seam**: the page batches **writes/intents up**; the hub fans **cross-tab updates + sync results down/across**. This answers note 09's cross-tab open thread — the SW is the join point *precisely because it is the one shared context*.

## Two honest forks ### The hub may not want to be the SW The SW's defining job is **fetch interception** (the URI face, note 04), and it is **killed aggressively on idle** — which fights the long-lived-shared-hub need. - **SharedWorker** is better suited to the dispatch-hub role: one persistent shared instance, direct `MessageChannel` ports, designed for shared state. Cost: weaker/absent support on some mobile browsers. - Or push the durable hub out to the **loopback** (note 02) — an OS node that never dies. - Likely split: **SharedWorker (or loopback) = dispatch + durability hub; SW = the cache/fetch face.** "Batches to the SW" generalizes to "batches to a shared hub," and which context hosts it is a live decision. (Transport options for page↔hub: `postMessage`, a dedicated `MessageChannel` port, or `BroadcastChannel` for direct tab↔tab when you do *not* need the durability coordinator in the loop.) ### Cross-tab merge is peer-merge, one scale down When two tabs batch writes to the same key, the hub must reconcile them — that is note 00/01's **conflict-resolution problem again**, but between tabs on one machine instead of nodes across a network. So the **coherence problem is self-similar across tiers**: ``` component ↔ component (in-page, ns) tab ↔ tab (via the hub, sub-ms) node ↔ node (via peers, network ms) ``` Same diffusion/percolation field (note 06), the same CRDT/LWW/leader machinery (note 01), **recurring at every boundary** — just at different speeds. The substrate is fractal: each tier is a smaller, faster copy of the whole.

## Open threads - **Hub placement** — SW vs SharedWorker vs loopback for the dispatch+durability role; per-platform (SharedWorker gaps on mobile → loopback or SW-only fallback). Maps to the per-OS subagents of bead-orchestrator duty-4. - **Transport** — `postMessage` vs `MessageChannel` vs `BroadcastChannel`; when the durability coordinator must be in the loop vs a pure tab↔tab shortcut. - **Cross-tab merge policy** — the hub runs the same per-resource-class conflict resolution as peer-merge; can the *same* CRDT/LWW code serve both the tab↔tab and node↔node boundaries (write once, run at every scale)? - **Backpressure page→hub** — when a page's local mutation rate outruns the hub's drain, where does the page-side buffer live and what sheds it.