Technical reference for OctoMY™'s wire protocols, packet formats, and communication layers.
Security Note
All established OctoMY sessions use XChaCha20-Poly1305 AEAD encryption with hybrid asymmetric key exchange (RSA-4096 or Ed25519 via the
ISigningKeyinterface). The multimagic field at the start of every packet enables rapid packet-type dispatch before any crypto operations, providing efficient DoS protection while maintaining full encryption for authenticated traffic.
| Node Type | Port | Description |
|---|---|---|
| Zoo | 8123 | NAT traversal and discovery service |
| Agent | 8124 | Robot node |
| Remote | 8125 | Controller app |
| Hub | 8126 | Fleet management |
Every packet begins with a SESSION_ID_TYPE (2-byte quint16) multimagic field. Reserved multimagic values identify protocol control packets; any value at or above MULTIMAGIC_LAST is interpreted as a remote session ID, making the packet courier data.
Handshake and data packets include a 4-byte protocol magic (OCTOMY_PROTOCOL_MAGIC = 0x0C701111) and a 4-byte protocol version (OCTOMY_PROTOCOL_VERSION_CURRENT = 1) immediately after the multimagic. The magic allows early rejection of non-OctoMY traffic; the version selects the QDataStream serialization format.
#define OCTOMY_PROTOCOL_MAGIC (0x0C701111)
#define OCTOMY_PROTOCOL_VERSION_CURRENT (1)
Each side of an established session chooses a local session ID (SESSION_ID_TYPE, 2 bytes). The remote session ID assigned by the peer is used as the multimagic value in courier data packets, so the receiver can look up the correct session in O(1).
OctoMY does not use TCP-style bit-flag headers. Instead, the first 2 bytes of every UDP datagram contain a multimagic value from the Multimagic enum that determines the packet type:
| Multimagic Value | Integer | Description |
|---|---|---|
MULTIMAGIC_IDLE |
0 | Keepalive / idle packet (no payload beyond multimagic) |
MULTIMAGIC_SYN |
1 | Handshake initiation |
MULTIMAGIC_SYNACK |
2 | Handshake response |
MULTIMAGIC_ACK |
3 | Handshake confirmation |
MULTIMAGIC_LAN_ANNOUNCE |
4 | LAN broadcast: "I exist" |
MULTIMAGIC_LAN_ANNOUNCE_ACK |
5 | LAN unicast: "I see you" |
MULTIMAGIC_LAN_IDENTITY_REQ |
6 | LAN unicast: request identity exchange |
MULTIMAGIC_LAN_IDENTITY_RESP |
7 | LAN unicast: identity exchange response |
MULTIMAGIC_NACK |
8 | Explicit session/trust rejection |
>= MULTIMAGIC_LAST |
>= 9 | Courier data (value is the remote session ID) |
The receiver dispatches on static_cast<Multimagic>(multimagic) in a switch; values not matching any known control type fall through to the courier-data path.
All handshake bodies are hybrid-encrypted with the recipient's public key (see Encryption below). The encrypted payload contains the fields listed; the outer packet is [multimagic:2][encrypted blob].
SYN Packet (initiator -> responder, encrypted with responder's public key):
| Field | Size | Description |
|---|---|---|
| Sender ID | 64 bytes | Full node identity hash (hex-decoded to 32 raw bytes + QDataStream framing) |
| Destination ID | 64 bytes | Intended recipient identity (prevents cross-nonce pollution on shared ports) |
| Desired Session ID | 2 bytes | Local session ID the initiator wants the responder to use |
| SYN Nonce | 4 bytes | Random SESSION_NONCE_TYPE challenge |
| Key Material | 32 bytes | Random AEAD key bytes for RSA/cross-algorithm key-transport path (only when encryption is enabled) |
SYN-ACK Packet (responder -> initiator, encrypted with initiator's public key):
| Field | Size | Description |
|---|---|---|
| Sender ID | 64 bytes | Responder's full node identity hash |
| Desired Session ID | 2 bytes | Local session ID the responder wants the initiator to use |
| Return Nonce | 4 bytes | Echo of the initiator's SYN nonce (proves receipt) |
| SYN-ACK Nonce | 4 bytes | Responder's own random challenge |
| Key Material Hash | 32 bytes | SHA256(keyMaterial) as confirmation of receipt (only when encryption is enabled) |
ACK Packet (initiator -> responder, encrypted with responder's public key):
| Field | Size | Description |
|---|---|---|
| Sender ID | 64 bytes | Initiator's full node identity hash |
| Return Nonce | 4 bytes | Echo of the responder's SYN-ACK nonce (proves receipt) |
Handshake packets use hybrid encryption: an asymmetric key exchange produces a one-time symmetric key, which is used for XChaCha20-Poly1305 AEAD encryption of the actual payload. The wire format depends on the recipient's key algorithm:
RSA hybrid (version byte 0x01):
[0x01][RSA-encrypted symKey : rsaKeyBytes][nonce : 24][AEAD ciphertext + tag : N + 16]
A random 32-byte symmetric key is generated, RSA-encrypted with the recipient's public key, and prepended. The payload is then AEAD-encrypted with that symmetric key.
Ed25519 hybrid (version byte 0x02):
[0x02][ephemeral Ed25519 pubkey : 32][nonce : 24][AEAD ciphertext + tag : N + 16]
An ephemeral Ed25519 keypair is generated. Both keys are converted to X25519. An ECDH key exchange with the recipient's X25519 public key produces the shared secret. The payload is AEAD-encrypted with that shared secret.
The version byte allows decrypt() to auto-detect the format and use the correct unwrapping path.
After the three-way handshake completes, both sides derive a shared session key for AEAD encryption of all subsequent courier data:
sessionKey = SHA256(sharedSecret_or_keyMaterial || synNonce_BE || synAckNonce_BE)
The derivation path depends on the key algorithms in use:
| Scenario | Path | Shared Secret Source |
|---|---|---|
| Both Ed25519 | ECDH | x25519KeyExchange(mySecret, theirPublic) |
| RSA or cross-algorithm | Key-transport | 32-byte keyMaterial sent in SYN |
Nonces are serialized big-endian before hashing.
Established sessions encrypt courier data using XChaCha20-Poly1305 with the derived session key:
[sessionID : 2][nonceCounter : 8][AEAD ciphertext + tag]
sessionID — the remote session ID (doubles as the multimagic value)nonceCounter — monotonically increasing 64-bit counter, sent in the clear so the receiver can reconstruct the 24-byte AEAD nonce: [counter:8 big-endian][zeros:16][courierID:4][payloadSize:2][courier payload]OctoMY does not have per-packet sequence numbers, ack bitmasks, or a transport-level retransmission layer. Reliability is handled per-courier at the application level:
| Aspect | Mechanism |
|---|---|
| Unreliable couriers | Fire-and-forget (sensors, joystick input) |
| Reliable couriers | Courier-specific ack/retransmit (e.g., BlobCourier uses chunk-level acknowledgments) |
| RTT measurement | SessionCourier heartbeat echo timestamps (16-byte payload: [myTimestamp:8][echoOfRemoteTimestamp:8]) |
| Connection detection | Heartbeat-based; session expires after configurable inactivity timeout (default 5 minutes) |
FlowControl uses RTT-based send-rate adjustment rather than a sliding window or congestion-avoidance algorithm:
| Mode | Condition | Send Rate |
|---|---|---|
| Good | RTT <= 250 ms | 30 Hz |
| Bad | RTT > 250 ms | 10 Hz |
Transitions:
LAN discovery uses its own simple binary format (not QDataStream). The magic is "OCTOMY" (6 ASCII bytes):
| Field | Offset | Size | Description |
|---|---|---|---|
| Magic | 0 | 6 bytes | "OCTOMY" |
| Version | 6 | 1 byte | Protocol version (0x01) |
| Packet Type | 7 | 2 bytes | Multimagic value (big-endian) |
| Node ID | 9 | 64 bytes | ASCII node identity hash (left-padded to 64 chars) |
| Node Type | 73 | 1 byte | Agent / Remote / Hub |
| Comms Port | 74 | 2 bytes | Comms channel port (big-endian) |
| Reply Port | 76 | 2 bytes | Ephemeral unicast reply port (big-endian) |
Total: 78 bytes.
Discovery packet types: MULTIMAGIC_LAN_ANNOUNCE (broadcast), MULTIMAGIC_LAN_ANNOUNCE_ACK (unicast response).
Identity exchange packets (MULTIMAGIC_LAN_IDENTITY_REQ, MULTIMAGIC_LAN_IDENTITY_RESP) use a larger variable-length format that includes the node's display name and public key in DER encoding.
For data larger than MTU (~1400 bytes):
Blob Header:
| Field | Size | Description |
|---|---|---|
| Blob ID | 4 bytes | Unique identifier |
| Total Size | 4 bytes | Total blob size |
| Chunk Count | 2 bytes | Number of chunks |
| Chunk Size | 2 bytes | Size per chunk |
Chunk Packet:
| Field | Size | Description |
|---|---|---|
| Blob ID | 4 bytes | Unique identifier |
| Chunk Index | 2 bytes | Chunk sequence number |
| Flags | 1 byte | FIRST, LAST, etc. |
| Data | Variable | Up to chunk size |
| ID Range | Purpose |
|---|---|
| 0-15 | System couriers |
| 16-127 | Built-in couriers |
| 128-255 | Custom couriers |
| ID | Courier | Description |
|---|---|---|
| 0 | System | Handshake, keepalive |
| 1 | Discovery | Peer discovery |
| 2 | AgentState | State synchronization |
| 3 | Sensors | Sensor data stream |
| 4 | Blob | Large data transfer |
| 5-15 | Reserved | Future system use |
The SessionCourier sends a 16-byte heartbeat payload at ~1 Hz:
| Field | Size | Description |
|---|---|---|
| My Timestamp | 8 bytes | Sender's current time (ms since epoch) |
| Echo Timestamp | 8 bytes | Last timestamp received from remote |
When the remote echoes back our timestamp, RTT is computed as now - echoedTimestamp. This drives FlowControl and connection-alive detection.
MULTIMAGIC_IDLE packets (multimagic only, no payload) serve as a lightweight keepalive for sessions that have no active couriers.
| Error | Response |
|---|---|
| Invalid protocol magic | Drop packet |
| Version mismatch | Drop packet, emit error |
| Decryption failed (0-byte plaintext) | Drop packet (logged at debug level for shared-port scenarios) |
| Unknown multimagic in control range | Drop packet |
| Session not established for courier data | Drop packet, emit error |
| Error | Detection | Recovery |
|---|---|---|
| Packet loss | Courier-specific (e.g., missing chunk acks) | Courier-specific retransmit |
| Connection lost | Heartbeat timeout | Re-handshake |
| NAT timeout | Hole closed | Re-punch |
| Session expired | 5-minute inactivity default | Session pruned; new handshake required |
| Threat | Protection |
|---|---|
| Eavesdropping | XChaCha20-Poly1305 AEAD encryption (handshake and courier data) |
| Replay attacks | 64-bit sliding window on nonce counter (checkAndUpdateRxNonce) |
| Impersonation | RSA or Ed25519 key verification via ISigningKey interface |
| DoS | Rate limiting; multimagic dispatch allows early rejection before crypto |
| Man-in-the-middle | Identicon verification during pairing (visual confirmation of key fingerprints) |