Wire protocol

The binary framing and opcodes shared by the data plane and the durability log.

Framing

Every message is a length-prefixed frame:

[4-byte big-endian body length][body]
body := [1-byte opcode][opcode-specific payload]

The same record format is used for:

  • client requests over the data-plane TCP port (7200),
  • replication messages between nodes,
  • the append-only durability log (so log records can be replayed and replicated verbatim).

Cypher queries use the same frames: a CYPHER request carries the database name, the query text, and JSON parameters; the reply is a RESPONSE whose value is the JSON result table.

Opcodes

OpcodeValuePayload
PUT1key (length-prefixed string) + value (length-prefixed octets)
GET2key
DELETE3key
SCAN4prefix
REPLICATE5seq (u64) + sub-op (u8) + put/delete payload
ACK6seq (u64, 0 if unused) + status (u8)
ERROR7message string
PING8none
PONG9none
SNAPSHOT10count (u32) + (key, value) pairs
RESPONSE11status (u8) + value octets + count (u32) + pairs
CYPHER12db (string) + query (string) + params (JSON octets)

Primitives

  • u8 / u32 / u64: unsigned big-endian integers.
  • length-prefixed string: u32 length + UTF-8/ASCII bytes.
  • octets: u32 length + raw bytes.
  • status: 0 = OK, 1 = not found.

Example: GET round trip

client -> node:  00 00 00 05 02 00 00 00 03 6b 65 79     (GET "key")
node   -> client: 00 00 00 0d 0b 00 00 00 00 00 00 00 03 76 61 6c
                                                     (RESPONSE status=0 value="val")

Implementation

The encoder/decoder live in src/protocol.lisp (encode-message, decode-message, frame-message). The TCP transport is in src/tcp.lisp ; hostnames are resolved with resolve-host, so peers can be addressed by DNS name in containers and Kubernetes.

NoteThe protocol uses a 4-byte length prefix, a 1-byte opcode, and an opcode-specific payload. The exact byte layout for each opcode is in the docstrings of src/protocol.lisp .