Session lifecycle
Handshake
Section titled “Handshake”- Transport authentication completes before protocol acceptance.
- The client sends
hellowith version, capabilities, required capabilities, JSON encoding, and optional bounded preferences. - The server replies with
welcomeor a protocol error. welcomecontains the accepting peer, negotiated capabilities, limits, heartbeat settings, and session ID. It contains no event cursors.- The client sends
readywith the session ID and optional host status. - After ready, the client installs its exact stream set with
stream.subscription.set.
stream.subscription.v1 is required by the shared client and server sessions. There is no separate handshake resume path.
Exact-set subscriptions
Section titled “Exact-set subscriptions”stream.subscription.set contains the session ID, a subscription ID, and StreamCursor[]. The server authorizes and resolves the complete requested set before replacing the old set. stream.subscription.updated reports one mode per stream:
live: cursor equals the stream head;replay: missing retained events will follow;snapshot_required: the cursor is ahead or older than retained history;unavailable: the server no longer knows the stream (for example a deleted conversation).
Accepted streams outside snapshot_required/unavailable become the exact active set. Replay is emitted before live events that arrived during replay. Streams degrade individually: one unknown stream must never reject the whole set, because that would silence live delivery for every other stream. Whole-set rejection is reserved for unauthorized or malformed requests, and a rejected update leaves the previous set active. Clients drop cursors for unavailable streams and clean up dependent state.
Heartbeat and close
Section titled “Heartbeat and close”Heartbeat data is only { sessionId, sentAt }. A graceful goodbye carries the session ID and bounded reason. Heartbeat timeout, invalid state, target failure, or unsafe outgoing overflow closes deterministically. Overflow requiring a fresh snapshot uses reason resync_required.
Browser identity and cursor epochs
Section titled “Browser identity and cursor epochs”The browser client ID is stored at nerve.protocol.clientId; the tab instance ID is stored at nerve.protocol.instanceId.
Workbench cursors are installed from snapshots and kept in application state. Cursor values advance only after successful reducer application.