crates/protocol/src/lib.rs. Encoding is
postcard — a compact, serde-based binary format
chosen because it’s small and fast (important on the input hot path).
Versioning:
PROTOCOL_VERSION is currently 1. Bump it on any breaking
wire change. The handshake exchanges versions so peers can refuse mismatches.Two channels
| Channel | Transport | Carries | Type |
|---|---|---|---|
| Input | UDP | mouse/keys, control signals, latency probes | InputPacket → InputMsg |
| Bulk | TCP | clipboard, files, handshake | BulkMsg |
Input channel
InputPacket
seq is a per-sender monotonic counter. The receiver drops any Events packet
whose seq is ≤ the highest seen — duplicate/straggler rejection without
blocking, the reason we use UDP. Control/ping messages bypass this check.
InputMsg
InputEvent
Events(Vec<..>) so a high
mouse polling rate doesn’t flood the network or cause “jumpiness” when polling
exceeds display refresh.
Portable Key
macOS and Windows use different raw keycodes, so we never send a raw scancode.
keymap.rs translates the native key to a portable Key enum on capture and
back on injection (the same approach Synergy/Deskflow use). Unmappable keys
become Key::Unknown(u32) and are dropped on injection.
Bulk channel
Framing
Each frame is au32 big-endian length prefix + the (optionally encrypted)
postcard bytes. Max frame size is 64 MiB (guards against a hostile peer
forcing a huge allocation).
BulkMsg
File transfer
FileBegin → many FileChunk (64 KiB each, written at offset) → FileEnd.
Offsets make it robust and resumable-in-principle. The receiver sanitizes the
filename (strips path components) to prevent path-traversal, and writes into
./received/.
Encryption framing
See Security for the crypto design. Framing specifics:- Bulk (TCP): the record is
seal(counter, aad=[], plaintext). The counter is implicit — TCP is ordered, so both peers keep in-sync send/recv counters starting at 0 and never transmit them. - Input (UDP): the wire is
seq(4 bytes, cleartext) || seal(seq, aad=seq, ciphertext). The sequence number doubles as the nonce counter and is bound in as associated data, so it can’t be tampered with. Packets that fail authentication are silently dropped.
Adding a new message
Bump the version if needed
If it changes the meaning of existing bytes, bump
PROTOCOL_VERSION and note
the change here.