docs: unify ws protocol + platform guides

This commit is contained in:
Peter Steinberger
2026-01-19 08:54:21 +00:00
parent 66193dab92
commit 35e7c62e78
6 changed files with 120 additions and 61 deletions

View File

@@ -5,7 +5,7 @@ read_when:
--- ---
# Gateway architecture # Gateway architecture
Last updated: 2026-01-05 Last updated: 2026-01-19
## Overview ## Overview
@@ -14,10 +14,9 @@ Last updated: 2026-01-05
- Control-plane clients (macOS app, CLI, web UI, automations) connect to the - Control-plane clients (macOS app, CLI, web UI, automations) connect to the
Gateway over **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 - **Nodes** (macOS/iOS/Android/headless) also connect over **WebSocket**, but
of the WebSocket control plane. declare `role: node` with explicit caps/commands.
- 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 **canvas host** (default `18793`) serves agenteditable HTML and A2UI. - A **canvas host** (default `18793`) serves agenteditable HTML and A2UI.
## Components and flows ## Components and flows
@@ -33,14 +32,13 @@ Last updated: 2026-01-05
- Send requests (`health`, `status`, `send`, `agent`, `system-presence`). - Send requests (`health`, `status`, `send`, `agent`, `system-presence`).
- Subscribe to events (`tick`, `agent`, `presence`, `shutdown`). - Subscribe to events (`tick`, `agent`, `presence`, `shutdown`).
### Nodes (macOS / iOS / Android) ### Nodes (macOS / iOS / Android / headless)
- Connect to the **bridge** (TCP JSONL) rather than the WS server. - Connect to the **same WS server** with `role: node`.
- 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: Protocol details:
- [Gateway protocol](/gateway/protocol) - [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.
@@ -77,6 +75,7 @@ Client Gateway
must match or the socket closes. must match or the socket closes.
- Idempotency keys are required for sideeffecting methods (`send`, `agent`) to - Idempotency keys are required for sideeffecting methods (`send`, `agent`) to
safely retry; the server keeps a shortlived dedupe cache. safely retry; the server keeps a shortlived dedupe cache.
- Nodes must include `role: "node"` plus caps/commands/permissions in `connect`.
## Protocol typing and codegen ## Protocol typing and codegen
@@ -92,6 +91,7 @@ Client Gateway
ssh -N -L 18789:127.0.0.1:18789 user@host ssh -N -L 18789:127.0.0.1:18789 user@host
``` ```
- The same handshake + auth token apply over the tunnel. - The same handshake + auth token apply over the tunnel.
- TLS + optional pinning can be enabled for WS in remote setups.
## Operations snapshot ## Operations snapshot

View File

@@ -1,18 +1,17 @@
--- ---
summary: "Bridge protocol (nodes): TCP JSONL, pairing, scoped RPC" summary: "Bridge protocol (legacy nodes): TCP JSONL, pairing, scoped RPC"
read_when: read_when:
- Building or debugging node clients (iOS/Android/macOS node mode) - Building or debugging node clients (iOS/Android/macOS node mode)
- Investigating pairing or bridge auth failures - Investigating pairing or bridge auth failures
- Auditing the node surface exposed by the gateway - Auditing the node surface exposed by the gateway
--- ---
# Bridge protocol (Node transport) # Bridge protocol (legacy node transport)
The Bridge protocol is a **narrow, authenticated** transport for nodes The Bridge protocol is a **legacy** node transport (TCP JSONL). New node clients
(iOS/Android/macOS node mode). It keeps the Gateway WS control plane loopbackonly should use the unified Gateway WebSocket protocol instead.
and exposes only a scoped set of methods for nodes.
If you are building an operator client (CLI, web UI, automations), use the If you are building an operator or node client, use the
[Gateway protocol](/gateway/protocol). [Gateway protocol](/gateway/protocol).
## Why we have both ## Why we have both

View File

@@ -8,12 +8,10 @@ read_when:
# Gateway protocol (WebSocket) # Gateway protocol (WebSocket)
The Gateway WS protocol is the **full control plane** for Clawdbot. It is The Gateway WS protocol is the **single control plane + node transport** for
loopback-only by default and is intended for local clients (CLI, web UI, Clawdbot. All clients (CLI, web UI, macOS app, iOS/Android nodes, headless
automations). nodes) connect over WebSocket and declare their **role** + **scope** at
handshake time.
If you are building a **node client** (iOS/Android/macOS node mode), use the
[Bridge protocol](/gateway/bridge-protocol) instead.
## Transport ## Transport
@@ -38,7 +36,11 @@ Client → Gateway:
"platform": "macos", "platform": "macos",
"mode": "operator" "mode": "operator"
}, },
"role": "operator",
"scopes": ["operator.read", "operator.write"],
"caps": [], "caps": [],
"commands": [],
"permissions": {},
"auth": { "token": "…" }, "auth": { "token": "…" },
"locale": "en-US", "locale": "en-US",
"userAgent": "clawdbot-cli/1.2.3" "userAgent": "clawdbot-cli/1.2.3"
@@ -57,6 +59,40 @@ Gateway → Client:
} }
``` ```
### Node example
```json
{
"type": "req",
"id": "…",
"method": "connect",
"params": {
"minProtocol": 3,
"maxProtocol": 3,
"client": {
"id": "ios-node",
"version": "1.2.3",
"platform": "ios",
"mode": "node"
},
"role": "node",
"scopes": [],
"caps": ["camera", "canvas", "screen", "location", "voice"],
"commands": ["camera.snap", "canvas.navigate", "screen.record", "location.get"],
"permissions": { "camera.capture": true, "screen.record": false },
"auth": { "token": "…" },
"locale": "en-US",
"userAgent": "clawdbot-ios/1.2.3",
"device": {
"id": "device_fingerprint",
"publicKey": "…",
"signature": "…",
"signedAt": 1737264000000
}
}
}
```
## Framing ## Framing
- **Request**: `{type:"req", id, method, params}` - **Request**: `{type:"req", id, method, params}`
@@ -65,6 +101,28 @@ Gateway → Client:
Side-effecting methods require **idempotency keys** (see schema). Side-effecting methods require **idempotency keys** (see schema).
## Roles + scopes
### Roles
- `operator` = control plane client (CLI/UI/automation).
- `node` = capability host (camera/screen/canvas/system.run).
### Scopes (operator)
Common scopes:
- `operator.read`
- `operator.write`
- `operator.admin`
- `operator.approvals`
- `operator.pairing`
### Caps/commands/permissions (node)
Nodes declare capability claims at connect time:
- `caps`: high-level capability categories.
- `commands`: command allowlist for invoke.
- `permissions`: granular toggles (e.g. `screen.record`, `camera.capture`).
The Gateway treats these as **claims** and enforces server-side allowlists.
## Versioning ## Versioning
- `PROTOCOL_VERSION` lives in `src/gateway/protocol/schema.ts`. - `PROTOCOL_VERSION` lives in `src/gateway/protocol/schema.ts`.
@@ -79,8 +137,22 @@ Side-effecting methods require **idempotency keys** (see schema).
- If `CLAWDBOT_GATEWAY_TOKEN` (or `--token`) is set, `connect.params.auth.token` - If `CLAWDBOT_GATEWAY_TOKEN` (or `--token`) is set, `connect.params.auth.token`
must match or the socket is closed. must match or the socket is closed.
## Device identity + pairing
- Nodes should include a stable device identity (`device.id`) derived from a
keypair fingerprint.
- Gateways issue tokens per device + role.
- Pairing approvals are required for new device IDs unless local auto-approval
is enabled.
## TLS + pinning
- TLS is supported for WS connections.
- Clients may optionally pin the gateway cert fingerprint (see `gateway.tls`
config and client TLS settings).
## Scope ## Scope
This protocol exposes the **full gateway API** (status, channels, models, This protocol exposes the **full gateway API** (status, channels, models, chat,
chat, agent, sessions, nodes, etc.). The exact surface is defined by the agent, sessions, nodes, approvals, etc.). The exact surface is defined by the
TypeBox schemas in `src/gateway/protocol/schema.ts`. TypeBox schemas in `src/gateway/protocol/schema.ts`.

View File

@@ -2,7 +2,7 @@
summary: "Android app (node): connection runbook + Canvas/Chat/Camera" summary: "Android app (node): connection runbook + Canvas/Chat/Camera"
read_when: read_when:
- Pairing or reconnecting the Android node - Pairing or reconnecting the Android node
- Debugging Android bridge discovery or auth - Debugging Android gateway discovery or auth
- Verifying chat history parity across clients - Verifying chat history parity across clients
--- ---
@@ -13,40 +13,38 @@ 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). - Protocols: [Gateway protocol](/gateway/protocol) (nodes + 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).
## Connection Runbook ## Connection Runbook
Android node app ⇄ (mDNS/NSD + TCP bridge) ⇄ **Gateway bridge** ⇄ (loopback WS) ⇄ **Gateway** Android node app ⇄ (mDNS/NSD + WebSocket) ⇄ **Gateway**
The Gateway WebSocket stays loopback-only (`ws://127.0.0.1:18789`). Android talks to the LAN-facing **bridge** (default `tcp://0.0.0.0:18790`) and uses Gateway-owned pairing. Android connects directly to the Gateway WebSocket (default `ws://<host>:18789`) and uses Gateway-owned pairing.
### Prerequisites ### Prerequisites
- You can run the Gateway on the “master” machine. - You can run the Gateway on the “master” machine.
- Android device/emulator can reach the gateway bridge: - Android device/emulator can reach the gateway WebSocket:
- Same LAN with mDNS/NSD, **or** - Same LAN with mDNS/NSD, **or**
- Same Tailscale tailnet using Wide-Area Bonjour / unicast DNS-SD (see below), **or** - Same Tailscale tailnet using Wide-Area Bonjour / unicast DNS-SD (see below), **or**
- Manual bridge host/port (fallback) - Manual gateway host/port (fallback)
- You can run the CLI (`clawdbot`) on the gateway machine (or via SSH). - You can run the CLI (`clawdbot`) on the gateway machine (or via SSH).
### 1) Start the Gateway (with bridge enabled) ### 1) Start the Gateway
Bridge is enabled by default (disable via `CLAWDBOT_BRIDGE_ENABLED=0`).
```bash ```bash
clawdbot gateway --port 18789 --verbose clawdbot gateway --port 18789 --verbose
``` ```
Confirm in logs you see something like: Confirm in logs you see something like:
- `bridge listening on tcp://0.0.0.0:18790 (node)` - `listening on ws://0.0.0.0:18789`
For tailnet-only setups (recommended for Vienna ⇄ London), bind the bridge to the gateway machines Tailscale IP instead: For tailnet-only setups (recommended for Vienna ⇄ London), bind the gateway to the tailnet IP:
- Set `bridge.bind: "tailnet"` in `~/.clawdbot/clawdbot.json` on the gateway host. - Set `gateway.bind: "tailnet"` in `~/.clawdbot/clawdbot.json` on the gateway host.
- Restart the Gateway / macOS menubar app. - Restart the Gateway / macOS menubar app.
### 2) Verify discovery (optional) ### 2) Verify discovery (optional)
@@ -54,7 +52,7 @@ For tailnet-only setups (recommended for Vienna ⇄ London), bind the bridge to
From the gateway machine: From the gateway machine:
```bash ```bash
dns-sd -B _clawdbot-bridge._tcp local. dns-sd -B _clawdbot._tcp local.
``` ```
More debugging notes: [Bonjour](/gateway/bonjour). More debugging notes: [Bonjour](/gateway/bonjour).
@@ -63,7 +61,7 @@ More debugging notes: [Bonjour](/gateway/bonjour).
Android NSD/mDNS discovery wont cross networks. If your Android node and the gateway are on different networks but connected via Tailscale, use Wide-Area Bonjour / unicast DNS-SD instead: Android NSD/mDNS discovery wont cross networks. If your Android node and the gateway are on different networks but connected via Tailscale, use Wide-Area Bonjour / unicast DNS-SD instead:
1) Set up a DNS-SD zone (example `clawdbot.internal.`) on the gateway host and publish `_clawdbot-bridge._tcp` records. 1) Set up a DNS-SD zone (example `clawdbot.internal.`) on the gateway host and publish `_clawdbot._tcp` records.
2) Configure Tailscale split DNS for `clawdbot.internal` pointing at that DNS server. 2) Configure Tailscale split DNS for `clawdbot.internal` pointing at that DNS server.
Details and example CoreDNS config: [Bonjour](/gateway/bonjour). Details and example CoreDNS config: [Bonjour](/gateway/bonjour).
@@ -72,14 +70,14 @@ Details and example CoreDNS config: [Bonjour](/gateway/bonjour).
In the Android app: In the Android app:
- The app keeps its bridge connection alive via a **foreground service** (persistent notification). - The app keeps its gateway connection alive via a **foreground service** (persistent notification).
- Open **Settings**. - Open **Settings**.
- Under **Discovered Bridges**, select your gateway and hit **Connect**. - Under **Discovered Gateways**, select your gateway and hit **Connect**.
- If mDNS is blocked, use **Advanced → Manual Bridge** (host + port) and **Connect (Manual)**. - If mDNS is blocked, use **Advanced → Manual Gateway** (host + port) and **Connect (Manual)**.
After the first successful pairing, Android auto-reconnects on launch: After the first successful pairing, Android auto-reconnects on launch:
- Manual endpoint (if enabled), otherwise - Manual endpoint (if enabled), otherwise
- The last discovered bridge (best-effort). - The last discovered gateway (best-effort).
### 4) Approve pairing (CLI) ### 4) Approve pairing (CLI)
@@ -117,7 +115,7 @@ The Android nodes Chat sheet uses the gateways **primary session key** (`m
If you want the node to show real HTML/CSS/JS that the agent can edit on disk, point the node at the Gateway canvas host. If you want the node to show real HTML/CSS/JS that the agent can edit on disk, point the node at the Gateway canvas host.
Note: nodes always use the standalone canvas host on `canvasHost.port` (default `18793`), bound to the bridge interface. Note: nodes use the standalone canvas host on `canvasHost.port` (default `18793`).
1) Create `~/clawd/canvas/index.html` on the gateway host. 1) Create `~/clawd/canvas/index.html` on the gateway host.

View File

@@ -3,7 +3,7 @@ summary: "iOS node app: connect to the Gateway, pairing, canvas, and troubleshoo
read_when: read_when:
- Pairing or reconnecting the iOS node - Pairing or reconnecting the iOS node
- Running the iOS app from source - Running the iOS app from source
- Debugging bridge discovery or canvas commands - Debugging gateway discovery or canvas commands
--- ---
# iOS App (Node) # iOS App (Node)
@@ -11,14 +11,13 @@ Availability: internal preview. The iOS app is not publicly distributed yet.
## What it does ## What it does
- Connects to a Gateway over the bridge (LAN or tailnet). - Connects to a Gateway over WebSocket (LAN or tailnet).
- Exposes node capabilities: Canvas, Screen snapshot, Camera capture, Location, Talk mode, Voice wake. - Exposes node capabilities: Canvas, Screen snapshot, Camera capture, Location, Talk mode, Voice wake.
- Receives `node.invoke` commands and reports node status events. - Receives `node.invoke` commands and reports node status events.
## Requirements ## Requirements
- Gateway running on another device (macOS, Linux, or Windows via WSL2). - Gateway running on another device (macOS, Linux, or Windows via WSL2).
- Bridge enabled (default).
- Network path: - Network path:
- Same LAN via Bonjour, **or** - Same LAN via Bonjour, **or**
- Tailnet via unicast DNS-SD (`clawdbot.internal.`), **or** - Tailnet via unicast DNS-SD (`clawdbot.internal.`), **or**
@@ -26,13 +25,13 @@ Availability: internal preview. The iOS app is not publicly distributed yet.
## Quick start (pair + connect) ## Quick start (pair + connect)
1) Start the Gateway (bridge enabled by default): 1) Start the Gateway:
```bash ```bash
clawdbot gateway --port 18789 clawdbot gateway --port 18789
``` ```
2) In the iOS app, open Settings and pick a discovered gateway (or enable Manual Bridge and enter host/port). 2) In the iOS app, open Settings and pick a discovered gateway (or enable Manual Host and enter host/port).
3) Approve the pairing request on the gateway host: 3) Approve the pairing request on the gateway host:
@@ -52,7 +51,7 @@ clawdbot gateway call node.list --params "{}"
### Bonjour (LAN) ### Bonjour (LAN)
The Gateway advertises `_clawdbot-bridge._tcp` on `local.`. The iOS app lists these automatically. The Gateway advertises `_clawdbot._tcp` on `local.`. The iOS app lists these automatically.
### Tailnet (cross-network) ### Tailnet (cross-network)
@@ -61,7 +60,7 @@ See [Bonjour](/gateway/bonjour) for the CoreDNS example.
### Manual host/port ### Manual host/port
In Settings, enable **Manual Bridge** and enter the gateway host + port (default `18790`). In Settings, enable **Manual Host** and enter the gateway host + port (default `18789`).
## Canvas + A2UI ## Canvas + A2UI

View File

@@ -57,7 +57,7 @@ The macOS app presents itself as a node. Common commands:
The node reports a `permissions` map so agents can decide whats allowed. The node reports a `permissions` map so agents can decide whats allowed.
Node service + app IPC: Node service + app IPC:
- When the headless node service is running (remote mode), it connects to the Gateway bridge. - When the headless node service is running (remote mode), it connects to the Gateway WS as a node.
- `system.run` executes in the macOS app (UI/TCC context) over a local Unix socket; prompts + output stay in-app. - `system.run` executes in the macOS app (UI/TCC context) over a local Unix socket; prompts + output stay in-app.
Diagram (SCI): Diagram (SCI):
@@ -161,11 +161,10 @@ the Node CLIs `dns-sd` based discovery.
## Remote connection plumbing (SSH tunnels) ## Remote connection plumbing (SSH tunnels)
When the macOS app runs in **Remote** mode, it opens SSH tunnels so local UI When the macOS app runs in **Remote** mode, it opens an SSH tunnel so local UI
components can talk to a remote Gateway as if it were on localhost. There are components can talk to a remote Gateway as if it were on localhost.
two independent tunnels:
### Control tunnel (Gateway control/WebSocket port) ### Control tunnel (Gateway WebSocket port)
- **Purpose:** health checks, status, Web Chat, config, and other control-plane calls. - **Purpose:** health checks, status, Web Chat, config, and other control-plane calls.
- **Local port:** the Gateway port (default `18789`), always stable. - **Local port:** the Gateway port (default `18789`), always stable.
- **Remote port:** the same Gateway port on the remote host. - **Remote port:** the same Gateway port on the remote host.
@@ -174,16 +173,8 @@ two independent tunnels:
- **SSH shape:** `ssh -N -L <local>:127.0.0.1:<remote>` with BatchMode + - **SSH shape:** `ssh -N -L <local>:127.0.0.1:<remote>` with BatchMode +
ExitOnForwardFailure + keepalive options. ExitOnForwardFailure + keepalive options.
### Node bridge tunnel (macOS node mode)
- **Purpose:** connect the macOS node to the Gateway **Bridge** protocol (TCP JSONL).
- **Remote port:** `gatewayPort + 1` (default `18790`), derived from the Gateway port.
- **Local port preference:** `CLAWDBOT_BRIDGE_PORT` or the default `18790`.
- **Behavior:** prefer the default bridge port for consistency; fall back to a
random local port if the preferred one is busy. The node then connects to the
resolved local port.
For setup steps, see [macOS remote access](/platforms/mac/remote). For protocol For setup steps, see [macOS remote access](/platforms/mac/remote). For protocol
details, see [Bridge protocol](/gateway/bridge-protocol). details, see [Gateway protocol](/gateway/protocol).
## Related docs ## Related docs