diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md index 1fa4cef3a..12c590b7a 100644 --- a/docs/concepts/architecture.md +++ b/docs/concepts/architecture.md @@ -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 diff --git a/docs/gateway/bridge-protocol.md b/docs/gateway/bridge-protocol.md index 00bcf8f7b..1e43ccc75 100644 --- a/docs/gateway/bridge-protocol.md +++ b/docs/gateway/bridge-protocol.md @@ -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 diff --git a/docs/gateway/protocol.md b/docs/gateway/protocol.md index c8bc13cac..505be4162 100644 --- a/docs/gateway/protocol.md +++ b/docs/gateway/protocol.md @@ -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`. diff --git a/docs/platforms/android.md b/docs/platforms/android.md index d096b15aa..1af367d0b 100644 --- a/docs/platforms/android.md +++ b/docs/platforms/android.md @@ -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://: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. diff --git a/docs/platforms/ios.md b/docs/platforms/ios.md index 6aacb28d9..8d39ae181 100644 --- a/docs/platforms/ios.md +++ b/docs/platforms/ios.md @@ -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 diff --git a/docs/platforms/macos.md b/docs/platforms/macos.md index 5e642ebac..9f74f26e2 100644 --- a/docs/platforms/macos.md +++ b/docs/platforms/macos.md @@ -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 :127.0.0.1:` 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