Protocol Overview
Wormkey uses a custom binary protocol over WebSocket to multiplex HTTP and WebSocket traffic between the Edge Gateway and CLI client.Transport: Persistent WebSocket connection (CLI → Edge Gateway)
Encoding: Binary frames with fixed 5-byte header
Version: v0 (subject to change)
Encoding: Binary frames with fixed 5-byte header
Version: v0 (subject to change)
Frame Format
All frames use a consistent structure:| Field | Size | Type | Description |
|---|---|---|---|
| Type | 1 byte | uint8 | Frame type identifier (0x01-0x0A) |
| StreamID | 4 bytes | uint32 (big-endian) | Stream identifier; 0 for control frames |
| Payload | Variable | bytes | Frame-specific data |
Stream ID Semantics
- Stream ID 0: Reserved for control frames (PING/PONG)
- Stream ID 1+: HTTP request/response streams
- Stream IDs are allocated by Edge Gateway
- Stream IDs are unique per tunnel connection
- Stream IDs are not reused within a connection
Frame Types
Control Frames
| Type | Value | Direction | Payload | Description |
|---|---|---|---|---|
| PING | 0x09 | Both | None | Keepalive ping |
| PONG | 0x0A | Both | None | Keepalive response |
HTTP Stream Frames
| Type | Value | Direction | Description |
|---|---|---|---|
| OPEN_STREAM | 0x01 | Edge → CLI | New incoming HTTP request |
| RESPONSE_HEADERS | 0x05 | CLI → Edge | HTTP response status and headers |
| STREAM_DATA | 0x02 | Both | Request or response body chunk |
| STREAM_END | 0x03 | Both | End of stream (normal close) |
| STREAM_CANCEL | 0x04 | Both | Abort stream (error or timeout) |
WebSocket Frames
| Type | Value | Direction | Description |
|---|---|---|---|
| WS_UPGRADE | 0x06 | Edge → CLI | Upgrade stream to WebSocket mode |
| WS_DATA | 0x07 | Both | Raw WebSocket frame data |
| WS_CLOSE | 0x08 | Both | WebSocket close |
Stream Lifecycle
HTTP Request/Response
- Edge receives HTTP request → Allocates stream ID → Sends
OPEN_STREAMwith method, path, headers - CLI receives
OPEN_STREAM→ Proxies request tolocalhost:port - CLI sends
RESPONSE_HEADERS→ Status code + HTTP headers - CLI sends
STREAM_DATA(0 or more times) → Response body chunks - CLI sends
STREAM_END→ Stream complete
The CLI streams response chunks as they arrive from localhost. This enables progressive rendering for streaming responses (e.g., React Server Components, Server-Sent Events).
Request Body Handling
For requests with body (POST, PUT, PATCH):STREAM_DATA frames until receiving STREAM_END, then makes the full HTTP request to localhost.
Stream Cancellation
Either side can cancel a stream:STREAM_CANCEL, the CLI must:
- Stop forwarding to localhost
- Abort any pending HTTP requests
- Send
STREAM_ENDorSTREAM_CANCELback (optional)
Payload Formats
OPEN_STREAM Payload
HTTP request serialized as:RESPONSE_HEADERS Payload
HTTP response status and headers:STREAM_DATA Payload
Raw bytes (request or response body chunk)STREAM_END Payload
Empty (no payload)STREAM_CANCEL Payload
Empty (no payload)WebSocket Upgrade
When Edge Gateway detectsUpgrade: websocket header:
- No
RESPONSE_HEADERSframe - Stream switches to raw bidirectional mode
WS_DATAcontains raw WebSocket frames (not HTTP-encoded)- Both sides can send
WS_DATAat any time - Stream ends with
WS_CLOSE
Keepalive (PING/PONG)
Both CLI and Edge Gateway implement keepalive:CLI Behavior
Edge Gateway Behavior
- Responds to PING with PONG immediately
- Does not initiate PINGs (CLI is responsible)
Connection Lifecycle
Initial Connection
- CLI: POST
/sessionsto Control Plane → ReceivessessionToken - CLI: WebSocket connect to Edge Gateway
/tunnel- Header:
Authorization: Bearer <sessionToken>
- Header:
- Edge Gateway: Validates token, binds
slug→ connection - Tunnel established: Ready to accept streams
Reconnection
If CLI loses connection:- CLI: Wait with exponential backoff (1s, 2s, 5s, 10s)
- CLI: Reconnect to
/tunnelwith samesessionToken - Edge Gateway: Replaces old connection for
slug - Old connection: Closed automatically
- Public URL: Remains valid (no interruption for users)
Sessions persist across reconnections. The same
sessionToken can be reused until the session expires or is closed.Session Expiration
Sessions have configurable expiration:- Control Plane rejects new tunnel connections
- Public URL returns 404
- CLI must create a new session
Protocol Limits
| Limit | Value | Enforced By |
|---|---|---|
| Max concurrent streams per tunnel | 100 | Edge Gateway |
| Max request body size | 10 MB | Edge Gateway |
| Idle timeout (no PING/PONG) | 5 minutes | Edge Gateway |
| PING interval | 25 seconds | CLI |
| PONG timeout | 30 seconds | CLI |
| Reconnect backoff | 1s, 2s, 5s, 10s | CLI |
| Max heartbeat failures | 2 | CLI |
Implementation Reference
See source code:- Frame encoding/decoding:
packages/cli/src/protocol.ts - CLI tunnel client:
packages/cli/src/tunnel.ts - Edge Gateway handler:
packages/gateway/main.go
Next Steps
- Component Details - How each component implements the protocol
- Architecture Overview - High-level system design