Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
153 lines
5.7 KiB
Markdown
153 lines
5.7 KiB
Markdown
---
|
|
summary: "WebSocket gateway architecture, components, and client flows"
|
|
read_when:
|
|
- Working on gateway protocol, clients, or transports
|
|
title: "Gateway architecture"
|
|
---
|
|
|
|
## Overview
|
|
|
|
- A single long-lived **Gateway** owns all messaging surfaces (WhatsApp via
|
|
Baileys, Telegram via grammY, Slack, Discord, Signal, iMessage, WebChat).
|
|
- 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/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.
|
|
- The **canvas host** is served by the Gateway HTTP server under:
|
|
- `/__openclaw__/canvas/` (agent-editable HTML/CSS/JS)
|
|
- `/__openclaw__/a2ui/` (A2UI host)
|
|
|
|
It uses the same port as the Gateway (default `18789`).
|
|
|
|
## Components and flows
|
|
|
|
### Gateway (daemon)
|
|
|
|
- Maintains provider connections.
|
|
- Exposes a typed WS API (requests, responses, server-push events).
|
|
- Validates inbound frames against JSON Schema.
|
|
- Emits events like `agent`, `chat`, `presence`, `health`, `heartbeat`, `cron`.
|
|
|
|
### Clients (mac app / CLI / web admin)
|
|
|
|
- One WS connection per client.
|
|
- Send requests (`health`, `status`, `send`, `agent`, `system-presence`).
|
|
- Subscribe to events (`tick`, `agent`, `presence`, `shutdown`).
|
|
|
|
### Nodes (macOS / iOS / Android / headless)
|
|
|
|
- Connect to the **same WS server** with `role: node`.
|
|
- Provide a device identity in `connect`; pairing is **device-based** (role `node`) and
|
|
approval lives in the device pairing store.
|
|
- Expose commands like `canvas.*`, `camera.*`, `screen.record`, `location.get`.
|
|
|
|
Protocol details: [Gateway protocol](/gateway/protocol)
|
|
|
|
### WebChat
|
|
|
|
- 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
|
|
clients.
|
|
|
|
## Connection lifecycle (single client)
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Client
|
|
participant Gateway
|
|
|
|
Client->>Gateway: req:connect
|
|
Gateway-->>Client: res (ok)
|
|
Note right of Gateway: or res error + close
|
|
Note left of Client: payload=hello-ok<br>snapshot: presence + health
|
|
|
|
Gateway-->>Client: event:presence
|
|
Gateway-->>Client: event:tick
|
|
|
|
Client->>Gateway: req:agent
|
|
Gateway-->>Client: res:agent<br>ack {runId, status:"accepted"}
|
|
Gateway-->>Client: event:agent<br>(streaming)
|
|
Gateway-->>Client: res:agent<br>final {runId, status, summary}
|
|
```
|
|
|
|
## Wire protocol (summary)
|
|
|
|
- Transport: WebSocket, text frames with JSON payloads.
|
|
- First frame **must** be `connect`.
|
|
- After handshake:
|
|
- Requests: `{type:"req", id, method, params}` → `{type:"res", id, ok, payload|error}`
|
|
- Events: `{type:"event", event, payload, seq?, stateVersion?}`
|
|
- `hello-ok.features.methods` / `events` are discovery metadata, not a
|
|
generated dump of every callable helper route.
|
|
- Shared-secret auth uses `connect.params.auth.token` or
|
|
`connect.params.auth.password`, depending on the configured gateway auth mode.
|
|
- Identity-bearing modes such as Tailscale Serve
|
|
(`gateway.auth.allowTailscale: true`) or non-loopback
|
|
`gateway.auth.mode: "trusted-proxy"` satisfy auth from request headers
|
|
instead of `connect.params.auth.*`.
|
|
- Private-ingress `gateway.auth.mode: "none"` disables shared-secret auth
|
|
entirely; keep that mode off public/untrusted ingress.
|
|
- 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`.
|
|
|
|
## Pairing and local trust
|
|
|
|
- All WS clients (operators + nodes) include a **device identity** on `connect`.
|
|
- New device IDs require pairing approval; the Gateway issues a **device token**
|
|
for subsequent connects.
|
|
- Direct local loopback connects can be auto-approved to keep same-host UX
|
|
smooth.
|
|
- OpenClaw also has a narrow backend/container-local self-connect path for
|
|
trusted shared-secret helper flows.
|
|
- Tailnet and LAN connects, including same-host tailnet binds, still require
|
|
explicit pairing approval.
|
|
- All connects must sign the `connect.challenge` nonce. Signature payload `v3`
|
|
also binds `platform` and `deviceFamily`; the gateway pins paired metadata on
|
|
reconnect and requires repair pairing for metadata changes.
|
|
- **Non-local** connects still require explicit approval.
|
|
- Gateway auth (`gateway.auth.*`) still applies to **all** connections, local or
|
|
remote.
|
|
|
|
Details: [Gateway protocol](/gateway/protocol), [Pairing](/channels/pairing),
|
|
[Security](/gateway/security).
|
|
|
|
## Protocol typing and codegen
|
|
|
|
- TypeBox schemas define the protocol.
|
|
- JSON Schema is generated from those schemas.
|
|
- Swift models are generated from the JSON Schema.
|
|
|
|
## Remote access
|
|
|
|
- Preferred: Tailscale or VPN.
|
|
- Alternative: SSH tunnel
|
|
|
|
```bash
|
|
ssh -N -L 18789:127.0.0.1:18789 user@gateway-host
|
|
```
|
|
|
|
- The same handshake + auth token apply over the tunnel.
|
|
- TLS + optional pinning can be enabled for WS in remote setups.
|
|
|
|
## Operations snapshot
|
|
|
|
- Start: `openclaw gateway` (foreground, logs to stdout).
|
|
- Health: `health` over WS (also included in `hello-ok`).
|
|
- Supervision: launchd/systemd for auto-restart.
|
|
|
|
## Invariants
|
|
|
|
- Exactly one Gateway controls a single Baileys session per host.
|
|
- Handshake is mandatory; any non-JSON or non-connect first frame is a hard close.
|
|
- Events are not replayed; clients must refresh on gaps.
|
|
|
|
## Related
|
|
|
|
- [Agent Loop](/concepts/agent-loop) — detailed agent execution cycle
|
|
- [Gateway Protocol](/gateway/protocol) — WebSocket protocol contract
|
|
- [Queue](/concepts/queue) — command queue and concurrency
|
|
- [Security](/gateway/security) — trust model and hardening
|