Overview
Crosslink’s wire protocol defines three layers, all JSON-encoded and sent as WebSocket text/binary messages:- Pairing frames — exchanged before any trust exists, to enroll a new device.
- The CLX1 handshake — an authenticated ephemeral key exchange that establishes a fresh session between an already-paired device and the host.
- Session frames — encrypted application messages (RPC calls, responses, events) once a session is established.
Pairing frames
Frames are plain JSON objects with akind discriminator (no envelope version field at this layer).
pair_hello / pair_ready
Sent when pairing happens directly over a socket to the host (no signaling broker) — resolves the scanned 9-digit code to a live session id.
pair_claim
Client sends a signed identity claim.
pair_challenge
Host replies with its own identity and the capabilities it is willing to grant, signed.
pair_complete / pair_done
Client confirms the Short Authentication String (SAS) with the user and sends the completion frame; host acknowledges.
pair_error
The CLX1 handshake
Once a device is paired, every reconnect runs a fresh authenticated key exchange (“CLX1”):sinit carries the client’s static X25519 key (sx), a fresh ephemeral X25519 key (epk), a 32-byte nonce (nc), a timestamp, and an Ed25519 signature over the transcript. sack carries the host’s ephemeral key (epk), its own 32-byte nonce (nh), and its signature. A srej { kind: "srej", code, message } frame rejects a handshake outright.
Key derivation
Session frames
Once a session’s traffic keys are derived, every application message is sealed independently with XChaCha20-Poly1305.- Algorithm: XChaCha20-Poly1305 (AEAD; the 16-byte Poly1305 tag is appended to
ct) - Nonce (
iv): 24 bytes, random per frame n: a strictly monotonic per-direction counter, starting at 1 — used as part of the AAD, so replay, reordering, and cross-direction substitution all fail authentication
Application messages
The plaintext inside each session frame is one application message, sharing a common envelope:v (protocol version, e.g. "1.0") and t (message type).
i is the request id, m the method name, p the payload, s a subscription id, e an event/method name depending on context. See Error Codes for the values err.e.code may take.
Version negotiation
Current protocol version: 1.0 (PROTOCOL_VERSION). A hello message carries the sender’s supported versions (highest last); the negotiated version is the highest one both sides support. There is no floor below which a connection silently downgrades.