Skip to content

Implementation (L3)

1. Read-through on miss

A read is served from the ReadCache on a hit or read through to the far node on a miss; the cached entry is then kept coherent by the down-channel. The cache holds data, not the ability to answer queries: messages and rows that flowed back, never a query engine. This is the line between "compute at the authority" and "cache what flowed back" — a near node never replicates the store and runs queries locally.

A split runtime serves a mail-list view whose rows are the authority server's computed query over an empty local store (crates/posthaste-server/tests/backend_link_split.rs::remote_runtime_serves_a_mail_list_view_from_the_backend); undo-history (current_summary) reads through too.

2. Retaining policy and coherence-by-eviction

A cached row/message is held until the down-channel says it changed, not until a TTL expires. The coherence task consumes AuthorityServerLink::subscribe and evicts a message's entry on any base assertion, so a cached summary is never stale (the next read re-fetches). Eviction under storage pressure is a separate policy decision (drop an entry; the next read re-fetches), distinct from coherence. A split runtime gets the retaining cache + the coherence task; co-located stays passthrough.

3. Split-process proofs

The seam is proven end to end with no mock on either side:

  • Write path — a Remote runtime forwards message.destroy across HTTP into the authority server's authoritative store (backend_link_split, // spec: docs/replication/backend-link/L1#3-the-backendapi-contract). destroy is used because setFlaggedState needs a local read (undo-history) a split runtime cannot do until the read path lands — the test that surfaced the read dependency concretely.
  • Read path — the read twin: a Remote runtime serves a mail-list view from the authority server over the link, and reads current_summary through, lifting the write-test blocker.

4. Co-located no-op short-circuits

The bundled deployment is byte-for-byte unchanged because every split-only mechanism degenerates co-located:

  • the read policy is passthrough (retain nothing, always read through) — no redundant cache;
  • the up-channel confirms synchronously, so the AuthorityServerPendingSet is empty and apply_pending_set_overlay is a no-op;
  • build_snapshot's pending-set fold short-circuits on an empty pending set.

This is "the same code, collapsed" (// spec: docs/replication/backend-link/L3#4-co-located-no-op-short-circuits), not a special case — co-located and split run one path with the split-only work gated to a non-empty pending set / retaining policy.

5. Hardening (partial)

[::state partial plan=eph/DESIGN-L2-deployment-topology#3-split-runtime-hardening]

The split-runtime path works but is not yet productized. Remaining: eviction under storage pressure (cache bounds/LRU), RuntimeCoverage reporting what a split runtime holds, reconnect/snapshot recovery on the down-channel, carrying the account id on assertions to avoid cross-account over-eviction, and sibling view replicas (detail/conversation) beyond the mail-list. Role-move optimism (moveToRole, into which the archive/trash sugar collapsed) is not folded yet (it needs account role→mailbox resolution).

6. Multi-runtime fan-in (partially realized)

[::state partial plan=eph/DESIGN-L2-deployment-topology#4-multi-runtime-fan-in]

The multi-runtime topology is specified at the seam contract (L1 §3.1: AuthorityServerLinkId, the per-runtime registry, settlement-routed-to-origin-runtime, per-runtime-idempotency, runtime-credential-per-runtime) and the mechanism (replication L1 §5.2, §10: per-near-node watermark, the multi-runtime hosted-authority-server preset). Realization is partial:

  1. ✅ a per-runtime registry on the AuthorityServer far node (AuthorityServerLinkId → mutation-id pairing + settlement sink), mirroring posthaste-runtime/src/far_end/links.rs's mutations_by_client_idAuthorityServer::forward_mutation_for dedups by (AuthorityServerLinkId, ClientMutationId) and assigns the RuntimeMutationId (S1). The co-located LocalAuthorityServer forwards under a minted id (the X = 1 case).
  2. ✅ per-runtime credential authentication in link_router, deriving AuthorityServerLinkIdLinkAuth::PerRuntime carries the token → AuthorityServerLinkId map; the middleware resolves the presented token and threads the id into forward_mutation_for (S2). There is no shared-bearer mode.
  3. ✅ emitting AuthorityServerFrame::Settlement on the down-channel and routing it to the originating runtime's stream (broadcast Base unchanged) — AuthorityServer::forward_mutation_for emits a Settlement onto a per-runtime sink; LocalAuthorityServer::subscribe_for merges the broadcast Base with that runtime's Settlements (S3). The near node currently retires its pending set via Base absorption (the state-before-event rule) and ignores AuthorityServerFrame::Settlement; consuming Settlement for explicit retirement (esp. the Failed case) is a near-node follow-up, not a far-node routing concern.
  4. (AuthorityServerLinkId, ClientMutationId) dedup at forward_mutation — folded into step 1 (S1).

The co-located path is the X = 1 case (a minted id, no auth), so these additions leave colocated-unchanged intact.

S4 (per-runtime reconnect/resume) is realized: a reconnecting runtime recreates its settlement channel and gets a fresh receiver — future Settlements resume (disconnect-window Settlements are lost; the near node reconciles via Base + the up-channel error). Bounded retention (S1) and the failed-mutation reject (so a retry re-applies) are in place. Remaining: the near node currently retires its pending set via Base absorption + the up-channel error and ignores AuthorityServerFrame::Settlement; consuming it for explicit retirement is the open item.

7. Assertions

ID Sev. Assertion
cache-is-coherent-base MUST A cached entry is held until the down-channel asserts it changed (evict-on-assertion), not until a TTL; eviction under pressure is a separate policy concern.
caches-data-not-queries MUST A near node caches the data that flowed back (messages and rows), not a query engine; new queries/pages are read-through to the authority.
split-proven-no-mock SHOULD The remote transport and the far-node link_router are proven to meet over loopback HTTP for both the write and read paths with no mock on either side.