Protocol Specification
ZAP wire format, encoding, and RPC semantics
Protocol Specification
This document specifies the ZAP wire protocol, including message encoding, RPC semantics, and transport requirements.
Wire Format
ZAP uses Cap'n Proto's standard wire format for all messages.
Message Structure
+------------------+------------------+
| Segment Count | Segment 0 Size |
| (4 bytes) | (4 bytes) |
+------------------+------------------+
| Segment 1 Size | Segment 2 Size |
| (4 bytes) | (4 bytes) |
+------------------+------------------+
| Padding |
| (if needed) |
+-------------------------------------+
| Segment 0 Data |
| (words) |
+-------------------------------------+
| Segment 1 Data |
| (words) |
+-------------------------------------+- All integers are little-endian
- Segments are 8-byte aligned
- First segment contains root pointer
RPC Message Format
RPC messages wrap Cap'n Proto method calls:
struct Message {
union {
unimplemented @0 :Void;
abort @1 :Exception;
call @2 :Call;
return @3 :Return;
finish @4 :Finish;
resolve @5 :Resolve;
release @6 :Release;
disembargo @7 :Disembargo;
bootstrap @8 :Bootstrap;
}
}
struct Call {
questionId @0 :UInt32;
target @1 :MessageTarget;
interfaceId @2 :UInt64;
methodId @3 :UInt16;
params @4 :Payload;
sendResultsTo :union {
caller @5 :Void;
yourself @6 :Void;
thirdParty @7 :RecipientId;
}
allowThirdPartyTailCall @8 :Bool = false;
}
struct Return {
answerId @0 :UInt32;
releaseParamCaps @1 :Bool = true;
union {
results @2 :Payload;
exception @3 :Exception;
canceled @4 :Void;
resultsSentElsewhere @5 :Void;
takeFromOtherQuestion @6 :UInt32;
acceptFromThirdParty @7 :ThirdPartyCapId;
}
}RPC Semantics
Connection Bootstrap
- Client sends
Bootstrapmessage to get root capability - Server returns
ReturnwithServerInfocapability - Client calls
init()on the capability
Client Server
| |
| Bootstrap(questionId=0) |
|------------------------------->|
| |
| Return(answerId=0, cap) |
|<-------------------------------|
| |
| Call(init, ClientInfo) |
|------------------------------->|
| |
| Return(ServerInfo) |
|<-------------------------------|Method Calls
Standard RPC call flow:
Client Server
| |
| Call(qid=1, listTools) |
|------------------------------->|
| |
| Return(aid=1, ToolList) |
|<-------------------------------|
| |
| Finish(qid=1) |
|------------------------------->|Pipelining
Cap'n Proto supports promise pipelining:
Client Server
| |
| Call(qid=1, subscribe, uri) |
|------------------------------->|
| |
| Call(qid=2, target=1.stream, |
| method=next) |
|------------------------------->|
| |
| Return(aid=1, stream cap) |
|<-------------------------------|
| |
| Return(aid=2, content) |
|<-------------------------------|Transport Requirements
TCP Transport
Default ZAP transport over TCP:
- Port: 9000 (default)
- Connection: Persistent, bidirectional
- Framing: Cap'n Proto standard
Connection URL format:
zap://host:port
zap+tls://host:port # With TLS
zap+pq://host:port # With post-quantum TLSUnix Socket Transport
For local IPC:
zap+unix:///path/to/socketWebSocket Transport
For browser clients:
zap+ws://host:port/path
zap+wss://host:port/path # With TLSBinary WebSocket frames contain Cap'n Proto messages.
Security
Post-Quantum Handshake
When using zap+pq://:
Client Server
| |
| PQHandshakeInit |
| - X25519 public key |
| - ML-KEM-768 public key |
| - Nonce |
| - Version |
|------------------------------->|
| |
| PQHandshakeResponse |
| - X25519 public key |
| - ML-KEM ciphertext |
| - Identity signature (opt) |
| - Server nonce |
|<-------------------------------|
| |
| [Derive shared secret] |
| sk = HKDF(X25519_DH || |
| ML-KEM_decap) |
| |
| PQChannelMessage (encrypted) |
|<------------------------------>|Channel Messages
After handshake, all RPC messages are encrypted:
struct PQChannelMessage {
sequence @0 :UInt64; # Replay protection
ciphertext @1 :Data; # AEAD encrypted
tag @2 :Data; # Authentication tag
associatedData @3 :Data; # Plaintext metadata
}Encryption: ChaCha20-Poly1305 or AES-256-GCM
Error Handling
Exception Types
struct Exception {
reason @0 :Text;
obsoleteIsCallersFault @1 :Bool;
obsoleteDurability @2 :UInt16;
type @3 :Type;
enum Type {
failed @0;
overloaded @1;
disconnected @2;
unimplemented @3;
}
}Error Propagation
failed: Operation failed, can retryoverloaded: Server busy, backoff and retrydisconnected: Connection lost, reconnectunimplemented: Method not supported
Flow Control
Backpressure
Cap'n Proto RPC has built-in flow control:
- Each Call has a
questionId - Server can only have N outstanding questions
- Client waits for Finish before new calls
Streaming
For large data, use streaming interfaces:
interface ResourceStream {
next @0 () -> (content :ResourceContent, done :Bool);
cancel @1 () -> ();
}Client pulls data with next() calls, controlling rate.
Versioning
Protocol Version
Exchanged during init:
struct ClientInfo {
name @0 :Text;
version @1 :Text;
}
struct ServerInfo {
name @0 :Text;
version @1 :Text;
capabilities @2 :Capabilities;
}Capability Negotiation
Server declares supported features:
struct Capabilities {
tools @0 :Bool;
resources @1 :Bool;
prompts @2 :Bool;
logging @3 :Bool;
}Clients should check capabilities before calling methods.
Interoperability
MCP Bridging
Gateway translates MCP JSON-RPC to ZAP:
MCP Server (stdio) Gateway ZAP Client
| | |
| JSON-RPC request | |
|<--------------------------| ZAP callTool |
| |<---------------------|
| JSON-RPC response | |
|-------------------------->| ZAP ToolResult |
| |--------------------->|JSON Embedding
JSON data embedded as Data fields:
struct ToolCall {
id @0 :Text;
name @1 :Text;
args @2 :Data; # JSON bytes
metadata @3 :Metadata;
}Parse JSON on application level, not wire level.
Last updated on