Gateway Protocol
The Gateway exposes a typed WebSocket RPC protocol for control-plane clients (operators) and companion nodes. The protocol is defined with TypeBox schemas and supports JSON text frames over WebSocket.
Transport and framing
Transport uses WebSocket with JSON payloads.
- The first frame sent by any client must be a
connectrequest. Any other first frame or non-JSON payload causes a hard close. - After a successful handshake:
- Requests use the shape
{type:"req", id, method, params?, idempotencyKey?}. - Responses use the shape
{type:"res", id, ok, payload?, error?}. - Events use the shape
{type:"event", event, payload, seq?, stateVersion?}.
- Requests use the shape
hello-ok.features.methods and hello-ok.features.events provide discovery metadata for available methods and events.
Connect handshake
The first message is a connect request. It carries:
- Protocol version range (
minProtocol/maxProtocol). - Client identity (
client.id,client.version,client.platform,client.mode). role:"operator"(default) or"node".auth: shared-secret token or password (underconnect.params.auth.tokenorconnect.params.auth.password), or omitted for identity-bearing modes.- For nodes:
caps,commands,permissions, and device identity. - Challenge signing for device identity (signatures bind platform and device family).
Auth modes are controlled by gateway.auth.* configuration. Identity-bearing modes (such as Tailscale) satisfy auth from headers. Direct local loopback connections may auto-approve in some pairing flows; non-local connections require explicit pairing approval and device tokens.
On success the server replies with hello-ok containing:
features.methods— array of callable method names (core plus active plugins).features.events— array of event names the client may receive.- Snapshot data (presence, health, and similar).
Request, response, and event frames
Every request carries a client-chosen id; responses echo it.
Side-effecting methods (send, agent, and similar) require an idempotencyKey for safe retry. The server maintains a short-lived deduplication cache.
Events are not replayed. Clients must refresh state on gaps using seq and stateVersion hints.
Some methods return an immediate accepted acknowledgment followed by a later final response and streaming events.
Method registry
Core methods are built-in and always present when the Gateway is running (health, status, sessions., chat., config., models., node., cron., and others).
Plugins contribute additional methods when active. These appear in hello-ok.features.methods only for active plugins.
Role and scope checks are applied before dispatch. Some methods are unavailable during early startup.
Idempotency
Idempotency keys are required for side-effecting methods. The server deduplicates within a short window; duplicate keys return the prior result without re-executing. Non-idempotent methods without a key are rejected.
Roles and scopes
Clients declare role at connect time:
"operator": control-plane clients (CLI, apps, web UI)."node": companion devices.
Operator scopes (derived from auth or declared in connect.scopes) include operator.admin, operator.write, and others. Scope checks are enforced per method. Nodes bypass most operator scope checks but remain subject to role and capability policy.
Unauthorized role or missing scope yields INVALID_REQUEST errors.
Operator versus node clients
Operators send control requests (agent, sessions.*, config.*, send, etc.) and receive events.
Nodes declare role:"node" plus capabilities at connect time. They use node.pair.*, node.invoke, node.event, and node.pending.* surfaces. Nodes expose device surfaces (camera, canvas, screen, location, voice, host commands) via node.invoke. Pairing is device-based; device tokens are issued on approval.
Both use the same WebSocket endpoint and framing, but nodes follow different authorization and capability rules.
Pairing, device identity, and tokens
All WebSocket clients present a device identity on connect.
New devices require explicit pairing approval. The Gateway issues a device token on successful pairing for subsequent reconnects. Tokens can be rotated or revoked.
Direct local loopback connects may auto-approve; non-local connects require explicit approval. All connects sign a challenge nonce.
Security and exposure notes
The default bind is loopback. Non-loopback binds require explicit auth (token or password) or a correctly configured trusted-proxy deployment.
Tailscale funnel requires gateway.auth.mode=password.
Private-ingress auth.mode:none must not be used on public or untrusted surfaces.
See Configuration for gateway.auth, bind, and Tailscale settings.
Related surfaces
HTTP surfaces (health and readiness probes, OpenAI-compatible endpoints, hooks, and plugin routes) are separate from the WebSocket RPC protocol.
See HTTP API for those surfaces.
The same WebSocket connection can carry both operator and node traffic once authenticated.
Invariants
- Exactly one Gateway per host controls messaging surfaces.
- Handshake is mandatory.
- Events are fire-and-forget; clients refresh on gaps.
- Protocol evolution is additive.