2026-01-12 04:44:14 +00:00
|
|
|
|
---
|
2026-01-19 08:54:21 +00:00
|
|
|
|
summary: "Bridge protocol (legacy nodes): TCP JSONL, pairing, scoped RPC"
|
2026-01-12 04:44:14 +00:00
|
|
|
|
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
|
2026-01-31 16:04:03 -05:00
|
|
|
|
title: "Bridge Protocol"
|
2026-01-12 04:44:14 +00:00
|
|
|
|
---
|
|
|
|
|
|
|
2026-01-19 08:54:21 +00:00
|
|
|
|
# Bridge protocol (legacy node transport)
|
2026-01-12 04:44:14 +00:00
|
|
|
|
|
2026-01-19 08:54:21 +00:00
|
|
|
|
The Bridge protocol is a **legacy** node transport (TCP JSONL). New node clients
|
|
|
|
|
|
should use the unified Gateway WebSocket protocol instead.
|
2026-01-12 04:44:14 +00:00
|
|
|
|
|
2026-01-19 08:54:21 +00:00
|
|
|
|
If you are building an operator or node client, use the
|
2026-01-12 04:44:14 +00:00
|
|
|
|
[Gateway protocol](/gateway/protocol).
|
|
|
|
|
|
|
2026-01-30 03:15:10 +01:00
|
|
|
|
**Note:** Current OpenClaw builds no longer ship the TCP bridge listener; this document is kept for historical reference.
|
2026-01-22 23:07:58 +00:00
|
|
|
|
Legacy `bridge.*` config keys are no longer part of the config schema.
|
|
|
|
|
|
|
2026-01-12 04:44:14 +00:00
|
|
|
|
## 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).
|
2026-01-16 05:28:33 +00:00
|
|
|
|
- Optional TLS (when `bridge.tls.enabled` is true).
|
2026-01-22 23:07:58 +00:00
|
|
|
|
- Legacy default listener port was `18790` (current builds do not start a TCP bridge).
|
2026-01-12 04:44:14 +00:00
|
|
|
|
|
2026-01-16 05:28:33 +00:00
|
|
|
|
When TLS is enabled, discovery TXT records include `bridgeTls=1` plus
|
2026-02-14 17:17:46 +01:00
|
|
|
|
`bridgeTlsSha256` as a non-secret hint. Note that Bonjour/mDNS TXT records are
|
|
|
|
|
|
unauthenticated; clients must not treat the advertised fingerprint as an
|
|
|
|
|
|
authoritative pin without explicit user intent or other out-of-band verification.
|
2026-01-16 05:28:33 +00:00
|
|
|
|
|
2026-01-12 04:44:14 +00:00
|
|
|
|
## Handshake + pairing
|
|
|
|
|
|
|
2026-01-31 21:13:13 +09:00
|
|
|
|
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`.
|
2026-01-12 04:44:14 +00:00
|
|
|
|
|
|
|
|
|
|
`hello-ok` returns `serverName` and may include `canvasHostUrl`.
|
|
|
|
|
|
|
|
|
|
|
|
## Frames
|
|
|
|
|
|
|
|
|
|
|
|
Client → Gateway:
|
2026-01-31 21:13:13 +09:00
|
|
|
|
|
2026-01-18 07:44:28 +00:00
|
|
|
|
- `req` / `res`: scoped gateway RPC (chat, sessions, config, health, voicewake, skills.bins)
|
2026-01-18 07:57:54 +00:00
|
|
|
|
- `event`: node signals (voice transcript, agent request, chat subscribe, exec lifecycle)
|
2026-01-12 04:44:14 +00:00
|
|
|
|
|
|
|
|
|
|
Gateway → Client:
|
2026-01-31 21:13:13 +09:00
|
|
|
|
|
2026-01-12 04:44:14 +00:00
|
|
|
|
- `invoke` / `invoke-res`: node commands (`canvas.*`, `camera.*`, `screen.record`,
|
|
|
|
|
|
`location.get`, `sms.send`)
|
|
|
|
|
|
- `event`: chat updates for subscribed sessions
|
|
|
|
|
|
- `ping` / `pong`: keepalive
|
|
|
|
|
|
|
2026-01-22 23:07:58 +00:00
|
|
|
|
Legacy allowlist enforcement lived in `src/gateway/server-bridge.ts` (removed).
|
2026-01-12 04:44:14 +00:00
|
|
|
|
|
2026-01-18 07:57:54 +00:00
|
|
|
|
## Exec lifecycle events
|
|
|
|
|
|
|
2026-01-22 00:49:02 +00:00
|
|
|
|
Nodes can emit `exec.finished` or `exec.denied` events to surface system.run activity.
|
|
|
|
|
|
These are mapped to system events in the gateway. (Legacy nodes may still emit `exec.started`.)
|
2026-01-18 07:57:54 +00:00
|
|
|
|
|
|
|
|
|
|
Payload fields (all optional unless noted):
|
2026-01-31 21:13:13 +09:00
|
|
|
|
|
2026-01-18 07:57:54 +00:00
|
|
|
|
- `sessionKey` (required): agent session to receive the system event.
|
|
|
|
|
|
- `runId`: unique exec id for grouping.
|
|
|
|
|
|
- `command`: raw or formatted command string.
|
|
|
|
|
|
- `exitCode`, `timedOut`, `success`, `output`: completion details (finished only).
|
|
|
|
|
|
- `reason`: denial reason (denied only).
|
|
|
|
|
|
|
2026-01-12 04:44:14 +00:00
|
|
|
|
## Tailnet usage
|
|
|
|
|
|
|
|
|
|
|
|
- Bind the bridge to a tailnet IP: `bridge.bind: "tailnet"` in
|
2026-01-30 03:15:10 +01:00
|
|
|
|
`~/.openclaw/openclaw.json`.
|
2026-01-12 04:44:14 +00:00
|
|
|
|
- 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.
|