MeridiansMeridians

Sync protocol — master ⇄ clients [Flow]

Source path: knowledge-base/diagrams/flows/sync-protocol.md

# Sync protocol — master ⇄ clients `[Flow]`

One daemon owns the record; N browsers connect to it. The sync layer is the **two-tier replication protocol** underneath [cache-coherence](cache-coherence.md): how a client's optimistic edit reaches the master, how the master's echo suppresses a re-write, and how a reconnecting client catches up.

```mermaid
flowchart TB
    subgraph Master["MASTER — owns the record (localhost / Electron / canonical web)"]
        direction TB
        Mut["applyLocalMutation<br/>gate → reduce → persist → attribute → echo"]
        Broker["**broker (Hub)** — one master sink, many client sinks<br/>+ lightweight registries / bounded asset cache"]
        Mut --> Broker
    end

    subgraph Client["CLIENT — renders synced state (tunnel origin)"]
        direction TB
        Opt["optimistic apply (local)"]
        IDB[("IndexedDB — rebuildable projection")]
    end

    Client -->|"① push action (relayed)"| Broker
    Broker -->|"② reduce + persist + echo"| Mut
    Broker -->|"③ SSE: domain + version token"| Client
    Client -->|"markRecordSynced(cls, id, version)"| Echo{"isRecordSynced?<br/>token matches?"}
    Echo -->|"yes — echo of my own write"| IDBonly(["write IndexedDB only<br/>NO daemon re-post"])
    Echo -->|"no — someone else's write"| Adopt(["adopt to store + IndexedDB"])

    Entries["every `entries` broadcast"] -->|"reconcileReplica:<br/>entry.updatedAt > ledger stamp?"| Stale{"provably<br/>stale?"}
    Stale -->|"yes — request (once per version)"| Broker
    Stale -->|"unprovable (ledger cold)"| Defer(["defer → replay when<br/>hydration stamps warm"])

    Recon(["reconnect / boot / foreground resume"]) -->|"active + version-gated doc + aux catch-up"| Broker
```

**Invariants**

- **Role is by origin, not config.** `getSyncRole()` → `master` for localhost / Electron / canonical web; `client` for a tunnel origin. A host reaching its *own* tunnel URL lands as a client, never a competing master — there is exactly one writer per world.
- **Echo suppression is token-based.** After a commit the master echoes a version token; the client `markRecordSynced(cls, id, version)`. When the SSE echo arrives, `isRecordSynced` matching the token means *"this is my own write"* → persist to IndexedDB only, don't re-post to the daemon. A non-matching token is someone else's write → adopt. This is what stops an infinite echo loop.
- **The broker is the fan-out.** One in-process Hub holds the single master sink and all client sinks, retaining only lightweight registries and a byte-bounded asset working set. Full documents stream live; reconnect catch-up requests them fresh from the canonical master. Clients never hold credentials or keys.
- **Catch-up is version-PROOF, and continuous.** The `record-synced` stamps double as a **version ledger** of what each side provably holds. On the master plane, reconnect, visible/focus resume, persisted bfcache restore, and network return share one coalesced pass: active domain, every doc class through `planStaleSweep`, and aux stores. The sweep pulls what's not held, pulls what canon's `updatedAt` moved past, *skips* what's held-but-unprovable (the storm guard), and re-runs when hydration warms the ledger. On the client plane, `reconcileReplica` runs the same plan against **every** `entries` broadcast, loop-guarded per `(id, version)`. In-sync replicas cost zero requests at every trigger. Boot reconciliation compares every doc: cache ahead → adopt to fs, record ahead → hydrate to cache ([cache-coherence](cache-coherence.md)).

**Where it lives:** `src/lib/server/sync/{role,broker,protocol}.ts`, `src/lib/server/record/catch-up.ts`, `src/lib/client/sync/{role,record-synced}.ts`, `src/lib/client/store/{hydration-signal.ts,hooks/use-record-stream.ts}`, `src/lib/client/cache/persistence.ts`. The daemon/host topology: [platform-model](../concepts/platform-model.md); the one-writer terminus: [engine-pipeline](engine-pipeline.md).
Open on GitHub

Raw Markdown source