docs: add protocol docs
This commit is contained in:
@@ -11,9 +11,11 @@ Last updated: 2026-01-05
|
|||||||
|
|
||||||
- A single long‑lived **Gateway** owns all messaging surfaces (WhatsApp via
|
- A single long‑lived **Gateway** owns all messaging surfaces (WhatsApp via
|
||||||
Baileys, Telegram via grammY, Slack, Discord, Signal, iMessage, WebChat).
|
Baileys, Telegram via grammY, Slack, Discord, Signal, iMessage, WebChat).
|
||||||
- All clients (macOS app, CLI, web UI, automations) connect to the Gateway over
|
- Control-plane clients (macOS app, CLI, web UI, automations) connect to the
|
||||||
**one transport: WebSocket** on the configured bind host (default
|
Gateway over **WebSocket** on the configured bind host (default
|
||||||
`127.0.0.1:18789`).
|
`127.0.0.1:18789`).
|
||||||
|
- **Nodes** (macOS/iOS/Android) use the **Bridge** protocol (TCP JSONL) instead
|
||||||
|
of the WebSocket control plane.
|
||||||
- One Gateway per host; it is the only place that opens a WhatsApp session.
|
- One Gateway per host; it is the only place that opens a WhatsApp session.
|
||||||
- A **bridge** (default `18790`) is used for nodes (macOS/iOS/Android).
|
- A **bridge** (default `18790`) is used for nodes (macOS/iOS/Android).
|
||||||
- A **canvas host** (default `18793`) serves agent‑editable HTML and A2UI.
|
- A **canvas host** (default `18793`) serves agent‑editable HTML and A2UI.
|
||||||
@@ -36,6 +38,10 @@ Last updated: 2026-01-05
|
|||||||
- Pair with the Gateway to receive a token.
|
- Pair with the Gateway to receive a token.
|
||||||
- Expose commands like `canvas.*`, `camera.*`, `screen.record`, `location.get`.
|
- Expose commands like `canvas.*`, `camera.*`, `screen.record`, `location.get`.
|
||||||
|
|
||||||
|
Protocol details:
|
||||||
|
- [Gateway protocol](/gateway/protocol)
|
||||||
|
- [Bridge protocol](/gateway/bridge-protocol)
|
||||||
|
|
||||||
### WebChat
|
### WebChat
|
||||||
- Static UI that uses the Gateway WS API for chat history and sends.
|
- Static UI that uses the Gateway WS API for chat history and sends.
|
||||||
- In remote setups, connects through the same SSH/Tailscale tunnel as other
|
- In remote setups, connects through the same SSH/Tailscale tunnel as other
|
||||||
|
|||||||
@@ -635,6 +635,8 @@
|
|||||||
"group": "Gateway & Ops",
|
"group": "Gateway & Ops",
|
||||||
"pages": [
|
"pages": [
|
||||||
"gateway",
|
"gateway",
|
||||||
|
"gateway/protocol",
|
||||||
|
"gateway/bridge-protocol",
|
||||||
"gateway/pairing",
|
"gateway/pairing",
|
||||||
"gateway/gateway-lock",
|
"gateway/gateway-lock",
|
||||||
"environment",
|
"environment",
|
||||||
|
|||||||
67
docs/gateway/bridge-protocol.md
Normal file
67
docs/gateway/bridge-protocol.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
summary: "Bridge protocol (nodes): TCP JSONL, pairing, scoped RPC"
|
||||||
|
read_when:
|
||||||
|
- Building or debugging node clients (iOS/Android/macOS node mode)
|
||||||
|
- Investigating pairing or bridge auth failures
|
||||||
|
- Auditing the node surface exposed by the gateway
|
||||||
|
---
|
||||||
|
|
||||||
|
# Bridge protocol (Node transport)
|
||||||
|
|
||||||
|
The Bridge protocol is a **narrow, authenticated** transport for nodes
|
||||||
|
(iOS/Android/macOS node mode). It keeps the Gateway WS control plane loopback‑only
|
||||||
|
and exposes only a scoped set of methods for nodes.
|
||||||
|
|
||||||
|
If you are building an operator client (CLI, web UI, automations), use the
|
||||||
|
[Gateway protocol](/gateway/protocol).
|
||||||
|
|
||||||
|
## Why we have both
|
||||||
|
|
||||||
|
- **Security boundary**: the bridge exposes a small allowlist instead of the
|
||||||
|
full gateway API surface.
|
||||||
|
- **Pairing + node identity**: node admission is owned by the gateway and tied
|
||||||
|
to a per-node token.
|
||||||
|
- **Discovery UX**: nodes can discover gateways via Bonjour on LAN, or connect
|
||||||
|
directly over a tailnet.
|
||||||
|
- **Loopback WS**: the full WS control plane stays local unless tunneled via SSH.
|
||||||
|
|
||||||
|
## Transport
|
||||||
|
|
||||||
|
- TCP, one JSON object per line (JSONL).
|
||||||
|
- Gateway owns the listener (default `18790`).
|
||||||
|
|
||||||
|
## Handshake + pairing
|
||||||
|
|
||||||
|
1) Client sends `hello` with node metadata + token (if already paired).
|
||||||
|
2) If not paired, gateway replies `error` (`NOT_PAIRED`/`UNAUTHORIZED`).
|
||||||
|
3) Client sends `pair-request`.
|
||||||
|
4) Gateway waits for approval, then sends `pair-ok` and `hello-ok`.
|
||||||
|
|
||||||
|
`hello-ok` returns `serverName` and may include `canvasHostUrl`.
|
||||||
|
|
||||||
|
## Frames
|
||||||
|
|
||||||
|
Client → Gateway:
|
||||||
|
- `req` / `res`: scoped gateway RPC (chat, sessions, config, health, voicewake)
|
||||||
|
- `event`: node signals (voice transcript, agent request, chat subscribe)
|
||||||
|
|
||||||
|
Gateway → Client:
|
||||||
|
- `invoke` / `invoke-res`: node commands (`canvas.*`, `camera.*`, `screen.record`,
|
||||||
|
`location.get`, `sms.send`)
|
||||||
|
- `event`: chat updates for subscribed sessions
|
||||||
|
- `ping` / `pong`: keepalive
|
||||||
|
|
||||||
|
Exact allowlist is enforced in `src/gateway/server-bridge.ts`.
|
||||||
|
|
||||||
|
## Tailnet usage
|
||||||
|
|
||||||
|
- Bind the bridge to a tailnet IP: `bridge.bind: "tailnet"` in
|
||||||
|
`~/.clawdbot/clawdbot.json`.
|
||||||
|
- Clients connect via MagicDNS name or tailnet IP.
|
||||||
|
- Bonjour does **not** cross networks; use manual host/port or wide-area DNS‑SD
|
||||||
|
when needed.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
Bridge is currently **implicit v1** (no min/max negotiation). Backward‑compat
|
||||||
|
is expected; add a bridge protocol version field before any breaking changes.
|
||||||
@@ -21,6 +21,10 @@ The design goal is to keep all network discovery/advertising in the **Node Gatew
|
|||||||
- **Bridge (direct transport)**: a LAN/tailnet-facing endpoint owned by the gateway that allows authenticated clients/nodes to call a scoped subset of gateway methods. The bridge exists so the gateway can remain loopback-only.
|
- **Bridge (direct transport)**: a LAN/tailnet-facing endpoint owned by the gateway that allows authenticated clients/nodes to call a scoped subset of gateway methods. The bridge exists so the gateway can remain loopback-only.
|
||||||
- **SSH transport (fallback)**: remote control by forwarding `127.0.0.1:18789` over SSH.
|
- **SSH transport (fallback)**: remote control by forwarding `127.0.0.1:18789` over SSH.
|
||||||
|
|
||||||
|
Protocol details:
|
||||||
|
- [Gateway protocol](/gateway/protocol)
|
||||||
|
- [Bridge protocol](/gateway/bridge-protocol)
|
||||||
|
|
||||||
## Why we keep both “direct” and SSH
|
## Why we keep both “direct” and SSH
|
||||||
|
|
||||||
- **Direct bridge** is the best UX on the same network and within a tailnet:
|
- **Direct bridge** is the best UX on the same network and within a tailnet:
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ CLAWDBOT_CONFIG_PATH=~/.clawdbot/b.json CLAWDBOT_STATE_DIR=~/.clawdbot-b clawdbo
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Protocol (operator view)
|
## Protocol (operator view)
|
||||||
|
- Full docs: [Gateway protocol](/gateway/protocol) and [Bridge protocol](/gateway/bridge-protocol).
|
||||||
- Mandatory first frame from client: `req {type:"req", id, method:"connect", params:{minProtocol,maxProtocol,client:{id,displayName?,version,platform,deviceFamily?,modelIdentifier?,mode,instanceId?}, caps, auth?, locale?, userAgent? } }`.
|
- Mandatory first frame from client: `req {type:"req", id, method:"connect", params:{minProtocol,maxProtocol,client:{id,displayName?,version,platform,deviceFamily?,modelIdentifier?,mode,instanceId?}, caps, auth?, locale?, userAgent? } }`.
|
||||||
- Gateway replies `res {type:"res", id, ok:true, payload:hello-ok }` (or `ok:false` with an error, then closes).
|
- Gateway replies `res {type:"res", id, ok:true, payload:hello-ok }` (or `ok:false` with an error, then closes).
|
||||||
- After handshake:
|
- After handshake:
|
||||||
|
|||||||
86
docs/gateway/protocol.md
Normal file
86
docs/gateway/protocol.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
---
|
||||||
|
summary: "Gateway WebSocket protocol: handshake, frames, versioning"
|
||||||
|
read_when:
|
||||||
|
- Implementing or updating gateway WS clients
|
||||||
|
- Debugging protocol mismatches or connect failures
|
||||||
|
- Regenerating protocol schema/models
|
||||||
|
---
|
||||||
|
|
||||||
|
# Gateway protocol (WebSocket)
|
||||||
|
|
||||||
|
The Gateway WS protocol is the **full control plane** for Clawdbot. It is
|
||||||
|
loopback-only by default and is intended for local clients (CLI, web UI,
|
||||||
|
automations).
|
||||||
|
|
||||||
|
If you are building a **node client** (iOS/Android/macOS node mode), use the
|
||||||
|
[Bridge protocol](/gateway/bridge-protocol) instead.
|
||||||
|
|
||||||
|
## Transport
|
||||||
|
|
||||||
|
- WebSocket, text frames with JSON payloads.
|
||||||
|
- First frame **must** be a `connect` request.
|
||||||
|
|
||||||
|
## Handshake (connect)
|
||||||
|
|
||||||
|
Client → Gateway:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "req",
|
||||||
|
"id": "…",
|
||||||
|
"method": "connect",
|
||||||
|
"params": {
|
||||||
|
"minProtocol": 3,
|
||||||
|
"maxProtocol": 3,
|
||||||
|
"client": {
|
||||||
|
"id": "cli",
|
||||||
|
"version": "1.2.3",
|
||||||
|
"platform": "macos",
|
||||||
|
"mode": "operator"
|
||||||
|
},
|
||||||
|
"caps": [],
|
||||||
|
"auth": { "token": "…" },
|
||||||
|
"locale": "en-US",
|
||||||
|
"userAgent": "clawdbot-cli/1.2.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Gateway → Client:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "res",
|
||||||
|
"id": "…",
|
||||||
|
"ok": true,
|
||||||
|
"payload": { "type": "hello-ok", "protocol": 3, "policy": { "tickIntervalMs": 15000 } }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Framing
|
||||||
|
|
||||||
|
- **Request**: `{type:"req", id, method, params}`
|
||||||
|
- **Response**: `{type:"res", id, ok, payload|error}`
|
||||||
|
- **Event**: `{type:"event", event, payload, seq?, stateVersion?}`
|
||||||
|
|
||||||
|
Side-effecting methods require **idempotency keys** (see schema).
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
- `PROTOCOL_VERSION` lives in `src/gateway/protocol/schema.ts`.
|
||||||
|
- Clients send `minProtocol` + `maxProtocol`; the server rejects mismatches.
|
||||||
|
- Schemas + models are generated from TypeBox definitions:
|
||||||
|
- `pnpm protocol:gen`
|
||||||
|
- `pnpm protocol:gen:swift`
|
||||||
|
- `pnpm protocol:check`
|
||||||
|
|
||||||
|
## Auth
|
||||||
|
|
||||||
|
- If `CLAWDBOT_GATEWAY_TOKEN` (or `--token`) is set, `connect.params.auth.token`
|
||||||
|
must match or the socket is closed.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This protocol exposes the **full gateway API** (status, providers, models,
|
||||||
|
chat, agent, sessions, nodes, etc.). The exact surface is defined by the
|
||||||
|
TypeBox schemas in `src/gateway/protocol/schema.ts`.
|
||||||
@@ -8,7 +8,7 @@ read_when:
|
|||||||
|
|
||||||
# Nodes
|
# Nodes
|
||||||
|
|
||||||
A **node** is a companion device (iOS/Android today) that connects to the Gateway over the **Bridge** and exposes a command surface (e.g. `canvas.*`, `camera.*`, `system.*`) via `node.invoke`.
|
A **node** is a companion device (iOS/Android today) that connects to the Gateway over the **Bridge** and exposes a command surface (e.g. `canvas.*`, `camera.*`, `system.*`) via `node.invoke`. Bridge protocol details: [Bridge protocol](/gateway/bridge-protocol).
|
||||||
|
|
||||||
macOS can also run in **node mode**: the menubar app connects to the Gateway’s bridge and exposes its local canvas/camera commands as a node (so `clawdbot nodes …` works against this Mac).
|
macOS can also run in **node mode**: the menubar app connects to the Gateway’s bridge and exposes its local canvas/camera commands as a node (so `clawdbot nodes …` works against this Mac).
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ read_when:
|
|||||||
- Gateway required: yes (run it on macOS, Linux, or Windows via WSL2).
|
- Gateway required: yes (run it on macOS, Linux, or Windows via WSL2).
|
||||||
- Install: [Getting Started](/start/getting-started) + [Pairing](/gateway/pairing).
|
- Install: [Getting Started](/start/getting-started) + [Pairing](/gateway/pairing).
|
||||||
- Gateway: [Runbook](/gateway) + [Configuration](/gateway/configuration).
|
- Gateway: [Runbook](/gateway) + [Configuration](/gateway/configuration).
|
||||||
|
- Protocols: [Bridge protocol](/gateway/bridge-protocol) (nodes) and [Gateway protocol](/gateway/protocol) (control plane).
|
||||||
|
|
||||||
## System control
|
## System control
|
||||||
System control (launchd/systemd) lives on the Gateway host. See [Gateway](/gateway).
|
System control (launchd/systemd) lives on the Gateway host. See [Gateway](/gateway).
|
||||||
|
|||||||
Reference in New Issue
Block a user