Expand description
§rindle-wire — the normalized subscription protocol
The wire vocabulary of Rindle Realtime’s “both wires, one protocol” rule
(RINDLE-REALTIME-DESIGN.md thesis + §4): the same normalized subscription is spoken
client←room and room←rindled, so its types live in one crate that every party
depends on — the native daemons (rindle-replica, rindle-server) on the emit
side, the room (rindle-room-core) on both sides, and, twinned in TypeScript,
@rindle/remote on the client. Three layers, one per module:
normalize— the fold: one transaction’s engine changes → deduplicated, table-taggednormalize::NormalizedOps (the payload).normalize_protocol— the envelope: the once-per-subscriptionnormalize_protocol::NormalizedHello, seq-0 snapshot, gap-freenormalize_protocol::NormalizedBatches; fingerprint, epoch/seq validation.wire_json— the JSON rendering the sockets actually carry (bare cells, camelCase, f64-safe counters, hex fingerprints) — byte-for-byte what@rindle/remotevalidates. The golden wire vectors are pinned here.
Beside the frame layers sits query_key — the canonical query identity every
materialization map dedups by (rindled’s manager and the room’s
materialize-on-presentation), so both ends of a wire agree on “the same query” —
and family_key, the same construction over a query’s template (its root
equality literals punched into holes), which is how the daemon recognizes the V
subscriptions that differ only in a literal as one parameterized query family
(design 310).
This crate must stay wasm-clean — the same discipline as the root rindle
crate: std-only, no C-toolchain/SQLite deps, no threads. And it holds no
transport: sockets and IO belong to the hosts (rindle-server, the Node shell,
the DO). The gate is cargo check -p rindle-wire --target wasm32-unknown-unknown;
keep it green.
Modules§
- family_
key FamilyKey— the identity of a parameterized query family (design 310 §3): theQueryKeyconstruction over a query’s template, the canonical AST with its eligible root-equality literals punched into holes. Two subscriptions are family-mates iff theirFamilyKeys are equal; the extracted literal tuple — theBinding, a [CanonKey] — is what distinguishes them.- normalize
- Normalized change events — the second serializer beside the engine’s flat path
(
rindle::flatten), for a local-first client that runs its own normalized base tables + local IVM. - normalize_
protocol - The normalized subscription protocol (
NORMALIZED-CHANGES-DESIGN.md§3) — the path-free twin of the engine’sflat_protocol. The envelope semantics are identical (a once-per-subscriptionNormalizedHello, a logical-seq-0 snapshot, then gap-free incrementalNormalizedBatches at seq 1, 2, …); only the payload differs — table-taggedNormalizedOps instead of path-taggedFlatChanges — and the hello is slimmer: flat per-table schemas (for positional-row alignment + anormalized_fp), not a nested hierarchical view schema (§3). - query_
key QueryKey— the canonical identity of a subscription query, the dedup key of every materialization map (rindled’sMaterializationManagerand the room’s materialize-on-presentation bookkeeping,RINDLE-REALTIME-DESIGN.md§4/§10.1). Two subscribers share one materialized pipeline iff their keys are equal; thefingerprint_hexform is thequeryKeystring that crosses the control planes. Moved here fromrindle-serverso the daemon and the room compute identical keys by construction.- wire_
json - The normalized protocol’s JSON wire rendering — byte-for-byte the shape the
@rindle/remoteclient validates (bare cells, camelCase keys, lowercaseoptags, hex fingerprints, f64-safe counters). Moved here fromrindle-replicaso every party on the wire shares ONE codec: the napi addon and the Rust daemon’s ws front emit with it (viarindle-replica’s re-export), and the room — the first Rust consumer of these frames (RINDLE-REALTIME-DESIGN.md§3) — parses with it. The..._from_jsondecoders are new with the room’s upstream leg; until they landed, decode existed only in TS (@rindle/remote/src/normalized.ts).