docs: unify ws protocol + platform guides
This commit is contained in:
@@ -5,7 +5,7 @@ read_when:
|
||||
---
|
||||
# Gateway architecture
|
||||
|
||||
Last updated: 2026-01-05
|
||||
Last updated: 2026-01-19
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -14,10 +14,9 @@ Last updated: 2026-01-05
|
||||
- Control-plane clients (macOS app, CLI, web UI, automations) connect to the
|
||||
Gateway over **WebSocket** on the configured bind host (default
|
||||
`127.0.0.1:18789`).
|
||||
- **Nodes** (macOS/iOS/Android) use the **Bridge** protocol (TCP JSONL) instead
|
||||
of the WebSocket control plane.
|
||||
- **Nodes** (macOS/iOS/Android/headless) also connect over **WebSocket**, but
|
||||
declare `role: node` with explicit caps/commands.
|
||||
- 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 agent‑editable HTML and A2UI.
|
||||
|
||||
## Components and flows
|
||||
@@ -33,14 +32,13 @@ Last updated: 2026-01-05
|
||||
- Send requests (`health`, `status`, `send`, `agent`, `system-presence`).
|
||||
- Subscribe to events (`tick`, `agent`, `presence`, `shutdown`).
|
||||
|
||||
### Nodes (macOS / iOS / Android)
|
||||
- Connect to the **bridge** (TCP JSONL) rather than the WS server.
|
||||
### Nodes (macOS / iOS / Android / headless)
|
||||
- Connect to the **same WS server** with `role: node`.
|
||||
- Pair with the Gateway to receive a token.
|
||||
- Expose commands like `canvas.*`, `camera.*`, `screen.record`, `location.get`.
|
||||
|
||||
Protocol details:
|
||||
- [Gateway protocol](/gateway/protocol)
|
||||
- [Bridge protocol](/gateway/bridge-protocol)
|
||||
|
||||
### WebChat
|
||||
- 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.
|
||||
- Idempotency keys are required for side‑effecting methods (`send`, `agent`) to
|
||||
safely retry; the server keeps a short‑lived dedupe cache.
|
||||
- Nodes must include `role: "node"` plus caps/commands/permissions in `connect`.
|
||||
|
||||
## Protocol typing and codegen
|
||||
|
||||
@@ -92,6 +91,7 @@ Client Gateway
|
||||
ssh -N -L 18789:127.0.0.1:18789 user@host
|
||||
```
|
||||
- The same handshake + auth token apply over the tunnel.
|
||||
- TLS + optional pinning can be enabled for WS in remote setups.
|
||||
|
||||
## Operations snapshot
|
||||
|
||||
|
||||
@@ -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:
|
||||
- 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)
|
||||
# Bridge protocol (legacy 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.
|
||||
The Bridge protocol is a **legacy** node transport (TCP JSONL). New node clients
|
||||
should use the unified Gateway WebSocket protocol instead.
|
||||
|
||||
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).
|
||||
|
||||
## Why we have both
|
||||
|
||||
@@ -8,12 +8,10 @@ read_when:
|
||||
|
||||
# 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.
|
||||
The Gateway WS protocol is the **single control plane + node transport** for
|
||||
Clawdbot. All clients (CLI, web UI, macOS app, iOS/Android nodes, headless
|
||||
nodes) connect over WebSocket and declare their **role** + **scope** at
|
||||
handshake time.
|
||||
|
||||
## Transport
|
||||
|
||||
@@ -38,7 +36,11 @@ Client → Gateway:
|
||||
"platform": "macos",
|
||||
"mode": "operator"
|
||||
},
|
||||
"role": "operator",
|
||||
"scopes": ["operator.read", "operator.write"],
|
||||
"caps": [],
|
||||
"commands": [],
|
||||
"permissions": {},
|
||||
"auth": { "token": "…" },
|
||||
"locale": "en-US",
|
||||
"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
|
||||
|
||||
- **Request**: `{type:"req", id, method, params}`
|
||||
@@ -65,6 +101,28 @@ Gateway → Client:
|
||||
|
||||
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
|
||||
|
||||
- `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`
|
||||
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
|
||||
|
||||
This protocol exposes the **full gateway API** (status, channels, models,
|
||||
chat, agent, sessions, nodes, etc.). The exact surface is defined by the
|
||||
This protocol exposes the **full gateway API** (status, channels, models, chat,
|
||||
agent, sessions, nodes, approvals, etc.). The exact surface is defined by the
|
||||
TypeBox schemas in `src/gateway/protocol/schema.ts`.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
summary: "Android app (node): connection runbook + Canvas/Chat/Camera"
|
||||
read_when:
|
||||
- Pairing or reconnecting the Android node
|
||||
- Debugging Android bridge discovery or auth
|
||||
- Debugging Android gateway discovery or auth
|
||||
- Verifying chat history parity across clients
|
||||
---
|
||||
|
||||
@@ -13,40 +13,38 @@ read_when:
|
||||
- Gateway required: yes (run it on macOS, Linux, or Windows via WSL2).
|
||||
- Install: [Getting Started](/start/getting-started) + [Pairing](/gateway/pairing).
|
||||
- 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 (launchd/systemd) lives on the Gateway host. See [Gateway](/gateway).
|
||||
|
||||
## 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
|
||||
|
||||
- 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 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).
|
||||
|
||||
### 1) Start the Gateway (with bridge enabled)
|
||||
|
||||
Bridge is enabled by default (disable via `CLAWDBOT_BRIDGE_ENABLED=0`).
|
||||
### 1) Start the Gateway
|
||||
|
||||
```bash
|
||||
clawdbot gateway --port 18789 --verbose
|
||||
```
|
||||
|
||||
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 machine’s 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.
|
||||
|
||||
### 2) Verify discovery (optional)
|
||||
@@ -54,7 +52,7 @@ For tailnet-only setups (recommended for Vienna ⇄ London), bind the bridge to
|
||||
From the gateway machine:
|
||||
|
||||
```bash
|
||||
dns-sd -B _clawdbot-bridge._tcp local.
|
||||
dns-sd -B _clawdbot._tcp local.
|
||||
```
|
||||
|
||||
More debugging notes: [Bonjour](/gateway/bonjour).
|
||||
@@ -63,7 +61,7 @@ More debugging notes: [Bonjour](/gateway/bonjour).
|
||||
|
||||
Android NSD/mDNS discovery won’t 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.
|
||||
|
||||
Details and example CoreDNS config: [Bonjour](/gateway/bonjour).
|
||||
@@ -72,14 +70,14 @@ Details and example CoreDNS config: [Bonjour](/gateway/bonjour).
|
||||
|
||||
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**.
|
||||
- Under **Discovered Bridges**, select your gateway and hit **Connect**.
|
||||
- If mDNS is blocked, use **Advanced → Manual Bridge** (host + port) and **Connect (Manual)**.
|
||||
- Under **Discovered Gateways**, select your gateway and hit **Connect**.
|
||||
- If mDNS is blocked, use **Advanced → Manual Gateway** (host + port) and **Connect (Manual)**.
|
||||
|
||||
After the first successful pairing, Android auto-reconnects on launch:
|
||||
- Manual endpoint (if enabled), otherwise
|
||||
- The last discovered bridge (best-effort).
|
||||
- The last discovered gateway (best-effort).
|
||||
|
||||
### 4) Approve pairing (CLI)
|
||||
|
||||
@@ -117,7 +115,7 @@ The Android node’s Chat sheet uses the gateway’s **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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ summary: "iOS node app: connect to the Gateway, pairing, canvas, and troubleshoo
|
||||
read_when:
|
||||
- Pairing or reconnecting the iOS node
|
||||
- Running the iOS app from source
|
||||
- Debugging bridge discovery or canvas commands
|
||||
- Debugging gateway discovery or canvas commands
|
||||
---
|
||||
# iOS App (Node)
|
||||
|
||||
@@ -11,14 +11,13 @@ Availability: internal preview. The iOS app is not publicly distributed yet.
|
||||
|
||||
## 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.
|
||||
- Receives `node.invoke` commands and reports node status events.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Gateway running on another device (macOS, Linux, or Windows via WSL2).
|
||||
- Bridge enabled (default).
|
||||
- Network path:
|
||||
- Same LAN via Bonjour, **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)
|
||||
|
||||
1) Start the Gateway (bridge enabled by default):
|
||||
1) Start the Gateway:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
@@ -52,7 +51,7 @@ clawdbot gateway call node.list --params "{}"
|
||||
|
||||
### 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)
|
||||
|
||||
@@ -61,7 +60,7 @@ See [Bonjour](/gateway/bonjour) for the CoreDNS example.
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ The macOS app presents itself as a node. Common commands:
|
||||
The node reports a `permissions` map so agents can decide what’s allowed.
|
||||
|
||||
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.
|
||||
|
||||
Diagram (SCI):
|
||||
@@ -161,11 +161,10 @@ the Node CLI’s `dns-sd` based discovery.
|
||||
|
||||
## Remote connection plumbing (SSH tunnels)
|
||||
|
||||
When the macOS app runs in **Remote** mode, it opens SSH tunnels so local UI
|
||||
components can talk to a remote Gateway as if it were on localhost. There are
|
||||
two independent tunnels:
|
||||
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.
|
||||
|
||||
### Control tunnel (Gateway control/WebSocket port)
|
||||
### Control tunnel (Gateway WebSocket port)
|
||||
- **Purpose:** health checks, status, Web Chat, config, and other control-plane calls.
|
||||
- **Local port:** the Gateway port (default `18789`), always stable.
|
||||
- **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 +
|
||||
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
|
||||
details, see [Bridge protocol](/gateway/bridge-protocol).
|
||||
details, see [Gateway protocol](/gateway/protocol).
|
||||
|
||||
## Related docs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user