Skip to Content
Wire Protocol

Wire Protocol Specification

Technical specification for Zentalk network communication binary formats.

Overview

The Zentalk wire protocol defines how nodes communicate at the network level. All communication is encrypted using TLS 1.3 and follows strict binary formatting for efficiency and security.

Transport Layer

PropertyValue
EncryptionTLS 1.3
TransportTCP
Relay Port9001
API Port443

Connection Lifecycle

1. TCP Connection └─ Client initiates TCP connection to node 2. TLS Handshake └─ TLS 1.3 with mutual authentication 3. Protocol Handshake └─ Exchange capabilities and versions 4. Session Active └─ Send/receive framed messages 5. Graceful Shutdown └─ DESTROY messages, then TCP FIN

Message Framing

All messages follow a consistent framing format for reliable parsing and length-prefixed reading.

┌────────────────────────────────────────┐ │ Length (4 bytes, big-endian) │ ├────────────────────────────────────────┤ │ Message Type (1 byte) │ ├────────────────────────────────────────┤ │ Payload (variable) │ └────────────────────────────────────────┘

Field Definitions

FieldSizeDescription
Length4 bytesTotal payload length (excluding this field)
Message Type1 byteIdentifies the message type
PayloadVariableType-specific data

Maximum Message Size

ConstraintValue
Max payload65,535 bytes
Max frame65,540 bytes
Typical relay cell542 bytes

Message Types

TypeCodeDirectionPurpose
HANDSHAKE0x01BidirectionalProtocol negotiation
RELAY_CREATE0x02Client → NodeCreate new circuit
RELAY_DATA0x03BidirectionalEncrypted relay data
RELAY_DESTROY0x04BidirectionalTear down circuit
PADDING0x05BidirectionalTraffic analysis resistance
PING0x06BidirectionalKeepalive request
PONG0x07BidirectionalKeepalive response

HANDSHAKE (0x01)

Initial protocol negotiation after TLS establishment.

┌────────────────────────────────────────┐ │ Protocol Version (2 bytes) │ ├────────────────────────────────────────┤ │ Capabilities (4 bytes, bitfield) │ ├────────────────────────────────────────┤ │ Node ID (32 bytes) │ ├────────────────────────────────────────┤ │ Timestamp (8 bytes) │ ├────────────────────────────────────────┤ │ Signature (64 bytes) │ └────────────────────────────────────────┘ Total: 110 bytes

RELAY_CREATE (0x02)

Request to create a new relay circuit.

┌────────────────────────────────────────┐ │ Circuit ID (4 bytes) │ ├────────────────────────────────────────┤ │ Handshake Type (1 byte) │ ├────────────────────────────────────────┤ │ Handshake Data (variable) │ └────────────────────────────────────────┘

RELAY_DATA (0x03)

Encrypted data traveling through a circuit.

┌────────────────────────────────────────┐ │ Circuit ID (4 bytes) │ ├────────────────────────────────────────┤ │ Cell Command (1 byte) │ ├────────────────────────────────────────┤ │ Encrypted Payload (509 bytes, padded) │ ├────────────────────────────────────────┤ │ Nonce (12 bytes) │ ├────────────────────────────────────────┤ │ Authentication Tag (16 bytes) │ └────────────────────────────────────────┘ Total: 542 bytes (fixed)

RELAY_DESTROY (0x04)

Tear down a circuit.

┌────────────────────────────────────────┐ │ Circuit ID (4 bytes) │ ├────────────────────────────────────────┤ │ Reason Code (1 byte) │ └────────────────────────────────────────┘ Total: 5 bytes

PADDING (0x05)

Dummy traffic for traffic analysis resistance.

┌────────────────────────────────────────┐ │ Random Data (variable, typically 509) │ └────────────────────────────────────────┘

PING/PONG (0x06/0x07)

Connection keepalive.

┌────────────────────────────────────────┐ │ Timestamp (8 bytes) │ ├────────────────────────────────────────┤ │ Nonce (8 bytes) │ └────────────────────────────────────────┘ Total: 16 bytes

Relay Cell Format

Relay cells carry encrypted data through circuits. All cells are fixed-size to prevent traffic analysis.

┌────────────────────────────────────────┐ │ Circuit ID (4 bytes) │ ├────────────────────────────────────────┤ │ Cell Command (1 byte) │ ├────────────────────────────────────────┤ │ Stream ID (2 bytes) │ ├────────────────────────────────────────┤ │ Digest (4 bytes) │ ├────────────────────────────────────────┤ │ Payload Length (2 bytes) │ ├────────────────────────────────────────┤ │ Payload Data (501 bytes, padded) │ ├────────────────────────────────────────┤ │ Nonce (12 bytes) │ ├────────────────────────────────────────┤ │ Authentication Tag (16 bytes) │ └────────────────────────────────────────┘ Total: 542 bytes (fixed)

Cell Commands

CommandCodeDescription
BEGIN0x01Open new stream
DATA0x02Stream data
END0x03Close stream
CONNECTED0x04Stream opened
EXTEND0x05Extend circuit
EXTENDED0x06Circuit extended
TRUNCATE0x07Truncate circuit
TRUNCATED0x08Circuit truncated

Encryption Layers

Messages are encrypted layer by layer, with each hop able to decrypt only its layer:

Original: [Payload] After Exit encryption: [AES-GCM(Key_exit, Payload || Exit_routing)] After Middle encryption: [AES-GCM(Key_middle, Exit_encrypted || Middle_routing)] After Guard encryption: [AES-GCM(Key_guard, Middle_encrypted || Guard_routing)] On wire: Guard-encrypted blob

Decryption Process

Guard Node: 1. Decrypt outer layer with Key_guard 2. Read routing info (next hop = Middle) 3. Forward to Middle Middle Node: 1. Decrypt layer with Key_middle 2. Read routing info (next hop = Exit) 3. Forward to Exit Exit Node: 1. Decrypt layer with Key_exit 2. Deliver to destination

Handshake Protocol

TLS 1.3 Setup

Client Server │ │ ├─────── ClientHello ──────────────►│ │ (supported ciphers) │ │ │ │◄────── ServerHello ───────────────┤ │ (selected cipher) │ │ Certificate │ │ CertificateVerify │ │ Finished │ │ │ ├─────── Certificate ──────────────►│ │ CertificateVerify │ │ Finished │ │ │ │ [Application Data] │ └───────────────────────────────────┘

Supported Cipher Suites

PriorityCipher Suite
1TLS_AES_256_GCM_SHA384
2TLS_CHACHA20_POLY1305_SHA256
3TLS_AES_128_GCM_SHA256

Node Authentication

After TLS establishment, nodes exchange HANDSHAKE messages:

1. Both nodes send HANDSHAKE message 2. Verify signature using node's Ed25519 key 3. Check timestamp is within 5 minutes 4. Verify capabilities are compatible 5. Session established

Capability Negotiation

BitCapabilityDescription
0RELAYCan relay traffic
1EXITCan be exit node
2GUARDCan be guard node
3KYBERSupports post-quantum
4PADDINGSupports traffic padding
5-31ReservedFuture use
Example: 0x0000001F = RELAY + EXIT + GUARD + KYBER + PADDING

API Message Format

The HTTP API uses JSON over HTTPS on port 443.

Request Format

POST /api/v1/{endpoint} HTTP/1.1 Host: api.zentalk.network Content-Type: application/json Authorization: Bearer {jwt_token} X-Request-ID: {uuid} X-Timestamp: {unix_ms} X-Signature: {ed25519_signature} { "action": "string", "params": { ... }, "nonce": "string" }

Response Format

HTTP/1.1 200 OK Content-Type: application/json X-Request-ID: {uuid} X-Response-Time: {ms} { "success": true, "data": { ... }, "timestamp": 1704067200000 }

Authentication Headers

HeaderRequiredDescription
AuthorizationYesBearer JWT token
X-Request-IDYesUnique request identifier
X-TimestampYesRequest timestamp (Unix ms)
X-SignatureConditionalEd25519 signature for sensitive operations

Common Endpoints

EndpointMethodPurpose
/api/v1/keys/bundleGETFetch user’s key bundle
/api/v1/keys/prekeysPOSTUpload one-time prekeys
/api/v1/messagesPOSTSubmit encrypted message
/api/v1/messagesGETRetrieve pending messages
/api/v1/nodesGETList relay nodes

Encoding Rules

Strict encoding rules ensure interoperability across implementations.

Integer Encoding

RuleDescription
Byte orderBig-endian (network order)
UnsignedAll integers are unsigned
Fixed widthUse exact specified width
Example: 0x00001234 (4 bytes) = 4660 decimal Byte layout: [0x00] [0x00] [0x12] [0x34] MSB LSB

String Encoding

RuleDescription
FormatUTF-8
Length prefix2 bytes (max 65,535 chars)
Null terminatorNot used
String "hello": ┌───────────┬────────────────────┐ │ 0x00 0x05 │ 0x68 0x65 0x6C ... │ │ (length) │ (UTF-8 bytes) │ └───────────┴────────────────────┘

Binary Data

RuleDescription
FormatRaw bytes
Length prefix4 bytes (for variable length)
Fixed fieldsNo length prefix
No base64Raw bytes on wire

Timestamps

RuleDescription
FormatUnix timestamp
PrecisionMilliseconds
Size8 bytes (uint64)
TimezoneUTC
Example: 2024-01-01 00:00:00 UTC Value: 1704067200000 (ms) Bytes: 0x00 0x00 0x01 0x8C 0xDB 0xC7 0xCC 0x00

Arrays

┌────────────────────────────────────────┐ │ Count (4 bytes) │ ├────────────────────────────────────────┤ │ Element 1 │ ├────────────────────────────────────────┤ │ Element 2 │ ├────────────────────────────────────────┤ │ ... │ └────────────────────────────────────────┘

Error Responses

Wire Protocol Errors

CodeNameDescription
0x00SUCCESSOperation completed
0x01PROTOCOL_ERRORInvalid message format
0x02CIRCUIT_NOT_FOUNDUnknown circuit ID
0x03CIRCUIT_DESTROYEDCircuit was torn down
0x04TIMEOUTOperation timed out
0x05RESOURCE_LIMITToo many circuits/streams
0x06AUTH_FAILEDAuthentication failed
0x07VERSION_MISMATCHIncompatible protocol version
0x08CRYPTO_ERROREncryption/decryption failed
0x09NODE_OVERLOADEDNode at capacity
0x0ASTREAM_CLOSEDStream no longer exists
0xFFINTERNAL_ERRORUnexpected server error

Error Message Format

┌────────────────────────────────────────┐ │ Error Code (1 byte) │ ├────────────────────────────────────────┤ │ Error Message Length (2 bytes) │ ├────────────────────────────────────────┤ │ Error Message (UTF-8, variable) │ ├────────────────────────────────────────┤ │ Context Data Length (2 bytes) │ ├────────────────────────────────────────┤ │ Context Data (optional, variable) │ └────────────────────────────────────────┘

API Error Response

{ "success": false, "error": { "code": "INVALID_SIGNATURE", "message": "Request signature verification failed", "details": { "expected_signer": "0x1234...", "timestamp": 1704067200000 } } }

HTTP Status Codes

StatusMeaning
200Success
400Bad Request (malformed)
401Unauthorized (auth failed)
403Forbidden (insufficient permissions)
404Not Found
429Rate Limited
500Internal Server Error
503Service Unavailable

Connection Management

Keepalive

ParameterValue
PING interval30 seconds
PONG timeout10 seconds
Max missed PINGs3

Reconnection

1. Connection lost 2. Wait: min(30s, 2^attempt * 1s) 3. Attempt reconnect 4. If failed, increment attempt counter 5. Max attempts: 10, then give up

Flow Control

ParameterValue
Max pending cells1000
Window size500 cells
Window update threshold250 cells
Last updated on