ZAP Protocol
Protocol Spec

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

  1. Client sends Bootstrap message to get root capability
  2. Server returns Return with ServerInfo capability
  3. 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 TLS

Unix Socket Transport

For local IPC:

zap+unix:///path/to/socket

WebSocket Transport

For browser clients:

zap+ws://host:port/path
zap+wss://host:port/path  # With TLS

Binary 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 retry
  • overloaded: Server busy, backoff and retry
  • disconnected: Connection lost, reconnect
  • unimplemented: 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

On this page