what travels, and the rule that keeps it safe
The wire: snapshots, diffs, events
Every frame on the socket is a Prism Document. There are three kinds, one envelope of metadata on the root, and one algebraic identity that makes a sparse diff always safe to apply or safe to discard.
Everything on the wire is a Document
There is no separate protocol format. Every frame the server pushes and every event the client sends is a serialized Prism Document (binary .prism on the socket). The transport is one WebSocket per session; the framing is kinogaki::ws, a small RFC 6455 implementation in Kinogaki Platform that also enforces the safety limits (frame-size cap, required masking on client frames, no fragmentation).
Three message kinds
| Kind | Direction | What it is |
|---|---|---|
| snapshot | server to client | a complete view-Document, the whole widget tree; the client adopts it wholesale |
| diff | server to client | a sparse overlay, only the Elements whose values changed; the client applies it onto its running view |
| event | client to server | a small Document naming an action Path and its values (a press, a filter pick, a form submit) |
A fresh connection gets a snapshot. After that, the server sends a diff when the view's structure is unchanged (same ordered Paths and types, only values differ) and a snapshot when the structure changed (an Element added, removed, reordered, or retyped). The reason is precise: Prism's overlay appends new Elements, so it cannot express a mid-sequence insert in place; a structural change must therefore be a full snapshot. This is the LiveView fingerprint rule, expressed in Prism's own composition.
The envelope
A diff is only safe to apply onto the exact base it was computed against. So the root Element of every frame carries an envelope of metadata that the client reads before it acts:
| Field | Meaning |
|---|---|
kind | snapshot or diff; a frame with no kind is a liveness pong, not a view |
epoch | the server launch; a new epoch means the client full-resyncs |
rev | this frame's revision, monotonic |
base_rev | the revision a diff applies onto |
count | the Element count after applying, a cheap post-apply divergence check |
The client applies a diff only when epoch matches and base_rev equals its current rev; otherwise it has diverged (a dropped or stale frame) and asks for a snapshot. After applying, if the resolved Element count does not match count, it resyncs. A diff layer often does not include the root (its content did not change), so the channel stamps a metadata-only root onto the layer; it overlays harmlessly onto the client's root.
The oracle
The whole scheme rests on one algebraic identity in Kinogaki Core:
overlay(base, diff(base, target)) == target
diff(base, target) is the minimal sparse layer; applying it onto the base reproduces the target exactly. Two consequences make the protocol safe:
- A diff is never more than an optimization of "send the whole thing." The worst case is always a correct full snapshot, so the client can fall back to one at any moment, and reconnect can just re-snapshot with no replay log.
- The client holds one current Document, not a history of diffs.
apply(base, patch)collapses a patch into the running view in place (it isoverlayfor a single layer), so memory does not grow with edits.
A concrete frame
A field edit on the issue board produces a diff of a few hundred bytes instead of the roughly 90 KB full view: only the changed Element travels, with the stamped envelope on the root. A filter switch changes the structure (the card list is replaced), so it ships as a snapshot. Both are ordinary Prism Documents; you can save either to a .prisma file and read it.
Events going back
An event is the same idea in the other direction: a small Document with an action Path (/issue/new, a filter pick) and any named string values (form fields). A form submit carries a monotonic sequence number so a submit can be matched to its acknowledgement. The client sends it now if connected, or queues it in the outbox to flush on reconnect (see the loop). The server reads the action, mutates the state, and the cycle closes.
Why Prism makes this cheap
A bespoke protocol would have to invent an identity scheme, a diff format, a serialization, and a resync story. Kinogaki invents none of them: identity is the Path, the diff format is overlay, the serialization is the Document codec, and resync is "send a snapshot." The wire is thin because the model is doing the work.