From 9056c43368d62ea12a38f832fa0329498bd59cf5 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Jul 2026 02:12:52 -0700 Subject: [PATCH] docs(gateway): add client-building and embedding guides (#111726) * docs(gateway): add client-building and embedding guides * docs(gateway): harden package rollout guidance --- docs/.i18n/glossary.zh-CN.json | 8 ++ docs/docs.json | 2 + docs/docs_map.md | 28 +++++ docs/gateway/clients.md | 197 +++++++++++++++++++++++++++++++++ docs/gateway/embedding.md | 161 +++++++++++++++++++++++++++ docs/gateway/external-apps.md | 34 +++--- docs/gateway/protocol.md | 21 ++++ 7 files changed, 436 insertions(+), 15 deletions(-) create mode 100644 docs/gateway/clients.md create mode 100644 docs/gateway/embedding.md diff --git a/docs/.i18n/glossary.zh-CN.json b/docs/.i18n/glossary.zh-CN.json index 5252dadb3ff..c946bf68e15 100644 --- a/docs/.i18n/glossary.zh-CN.json +++ b/docs/.i18n/glossary.zh-CN.json @@ -131,6 +131,14 @@ "source": "Gateway RPC reference", "target": "Gateway RPC 参考" }, + { + "source": "Building a Gateway client", + "target": "构建 Gateway 客户端" + }, + { + "source": "Embedding OpenClaw", + "target": "嵌入 OpenClaw" + }, { "source": "Secure file operations", "target": "安全文件操作" diff --git a/docs/docs.json b/docs/docs.json index 43d1203c906..1c93a25ffff 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1686,6 +1686,8 @@ "group": "Protocols and APIs", "pages": [ "gateway/protocol", + "gateway/clients", + "gateway/embedding", "gateway/bridge-protocol", "gateway/openai-http-api", "gateway/openresponses-http-api", diff --git a/docs/docs_map.md b/docs/docs_map.md index 350e3efe62c..041dbde9f73 100644 --- a/docs/docs_map.md +++ b/docs/docs_map.md @@ -3247,6 +3247,20 @@ Do not edit it by hand; run `pnpm docs:map:gen`. - H2: Troubleshooting - H2: Related +## gateway/clients.md + +- Route: /gateway/clients +- Headings: + - H2: Install the packages + - H2: Choose scopes and pair the device + - H2: Advertise client capabilities + - H2: Recover state after reconnect + - H2: Use history metadata and stable anchors + - H2: Subscribe instead of polling usage + - H2: Backfill exec approvals + - H2: Track protocol versions + - H2: Related + ## gateway/cloud-workers.md - Route: /gateway/cloud-workers @@ -3496,6 +3510,19 @@ Do not edit it by hand; run `pnpm docs:map:gen`. - H2: Detailed behavior and rationale - H2: Related +## gateway/embedding.md + +- Route: /gateway/embedding +- Headings: + - H2: Start the child with an embedding preset + - H3: Electron shell snapshot warning + - H2: Handle invalid config by exit code + - H2: Wait for protocol readiness + - H2: Interpret restart and shutdown + - H2: Use RPC instead of state files + - H2: Install; do not flatten + - H2: Related + ## gateway/external-apps.md - Route: /gateway/external-apps @@ -3793,6 +3820,7 @@ Do not edit it by hand; run `pnpm docs:map:gen`. - Route: /gateway/protocol - Headings: + - H2: npm packages - H2: Transport and framing - H2: Handshake - H3: Worker role and closed protocol diff --git a/docs/gateway/clients.md b/docs/gateway/clients.md new file mode 100644 index 00000000000..7542ee001f6 --- /dev/null +++ b/docs/gateway/clients.md @@ -0,0 +1,197 @@ +--- +summary: "Build a third-party operator or WebChat client for the Gateway WebSocket protocol" +read_when: + - Building an operator, dashboard, or WebChat client outside the OpenClaw repository + - Implementing Gateway reconnect, history, approvals, or device pairing + - Updating a third-party client for a new Gateway wire version +title: "Building a Gateway client" +--- + +Use the published Gateway packages to build operator dashboards, WebChat clients, +and other third-party applications. This guide covers the client lifecycle around +the wire contract: authentication, capabilities, reconnect recovery, history, +subscriptions, and version upgrades. + +For frame shapes, the handshake, errors, and the complete method surface, read the +[Gateway protocol specification](https://docs.openclaw.ai/gateway/protocol). + +## Install the packages + +```bash +npm install @openclaw/gateway-client @openclaw/gateway-protocol +``` + + +These packages ship with OpenClaw release trains. During the initial rollout, npm +may return `E404` until the first package-bearing OpenClaw release is published; +install them only after the registry pages below resolve. + + +- [`@openclaw/gateway-protocol`](https://www.npmjs.com/package/@openclaw/gateway-protocol) + provides schemas, runtime validators, TypeScript types, client identity and + capability registries, structured error readers, and protocol version constants. + Its npm tarball also includes the generated + [`protocol.schema.json`](https://unpkg.com/@openclaw/gateway-protocol/protocol.schema.json) + machine-readable contract. +- [`@openclaw/gateway-client`](https://www.npmjs.com/package/@openclaw/gateway-client) + is the reference connection implementation. Import the package root for the Node + client and `@openclaw/gateway-client/browser` for the browser-safe protocol, + device-auth, and reconnect helpers. + +The Node entry owns its WebSocket transport. A browser host supplies a WebSocket +adapter plus persistent storage and signing callbacks for the device identity and +device token. + +## Choose scopes and pair the device + +A full interactive chat client that also renders approval prompts should request +`role: "operator"` with these scopes: + +| Scope | Use it for | +| -------------------- | ----------------------------------------------------------------------------------------- | +| `operator.read` | `chat.history`, `sessions.list`, `sessions.subscribe`, model status, and read-only events | +| `operator.write` | `chat.send` and ordinary session mutations | +| `operator.approvals` | Listing, displaying, and resolving exec or plugin approvals | + +Add `operator.questions` only if the client handles interactive questions, +`operator.pairing` only if it manages paired devices or nodes, and +`operator.admin` only for administrative operations such as `config.patch`. +The [operator scopes reference](https://docs.openclaw.ai/gateway/operator-scopes) +defines the complete method and approval-time rules. + +Do not create a per-client bearer token by hand-editing `openclaw.json`. Configure +the Gateway's shared bootstrap authentication with `openclaw configure --section +gateway` or the `openclaw onboard --gateway-auth ...` options, then let device +pairing mint the client token: + +1. Persist an Ed25519 device identity in the client. +2. Wait for `connect.challenge`, sign the challenge-bound device payload, and send + `connect` with the requested operator role, scopes, and the shared Gateway token + or password for bootstrap authentication. +3. If the Gateway returns structured `PAIRING_REQUIRED` details, show the request + ID and pause or retry according to `error.details.recommendedNextStep`. +4. On the Gateway host, review the request with `openclaw devices list`, then + approve that exact current request with `openclaw devices approve `. +5. Reconnect and persist `hello-ok.auth.deviceToken` with the negotiated role and + scopes. Use that device token for later connections. + +Scope or role upgrades create a new pending pairing request. Token rotation cannot +expand the approved pairing contract. See the +[Devices CLI](https://docs.openclaw.ai/cli/devices) for approval, rotation, and +revocation commands. + +## Advertise client capabilities + +`connect.params.caps` describes optional behavior the client can consume. It does +not grant authorization. Import names from `GATEWAY_CLIENT_CAPS` instead of +duplicating string literals: + +```ts +import { GATEWAY_CLIENT_CAPS } from "@openclaw/gateway-protocol/client-info"; + +const caps = [GATEWAY_CLIENT_CAPS.TOOL_EVENTS]; +``` + +The current registry contains `approvals`, `exec-approvals`, `inline-widgets`, +`run-tool-bindings`, `session-scoped-events`, `plugin-approvals`, +`task-suggestions`, `terminal-offset-seq`, `tool-events`, and `ui-commands`. +Advertise only capabilities the client actually implements. + + +`tool-events` gates live tool-execution streaming. The Gateway registers only +connections that advertise this capability as recipients for a run's structured +tool events. Without it, the connection receives no live tool events and the +handshake does not report an error. + + +Capability-gated agent tools are a separate use of the same declaration. If an +agent tool requires a client capability, the Gateway omits that tool unless the +originating client advertised every required capability. + +## Recover state after reconnect + +Treat every successful reconnect as a new projection over durable history and +current in-memory run state: + +1. Re-establish `sessions.subscribe` and the selected session's + `sessions.messages.subscribe` subscription. +2. Call `chat.history` for the selected `sessionKey` and replace local persisted + rows with the returned `messages` projection. +3. If `inFlightRun` is present, adopt its `runId`, buffered `text`, and optional + `plan`. Adopt the run even when `text` is empty. +4. Read `sessionInfo.hasActiveRun` and `sessionInfo.activeRunIds`. Prefer exact + membership in `activeRunIds` when deciding whether a retained run still owns + the streaming UI. A true `hasActiveRun` with no listed ID can represent another + active runtime projection. +5. Reconcile subsequent `agent` events by `payload.runId` and `payload.seq`. + Maintain the highest accepted sequence independently for each run, ignore an + already-seen or lower sequence, and treat a forward gap as a reason to reload + authoritative history. + +The outer event frame also has an optional `seq`, which orders events on the +current WebSocket connection. It resets with a new connection. The `seq` inside +an `agent` event payload is assigned per run and orders that run's lifecycle, +assistant, plan, tool, and other stream events. + +## Use history metadata and stable anchors + +Rows returned by `chat.history` can carry an `__openclaw` metadata envelope: + +- `id` is the transcript entry identity. Use it for anchored history requests, + but not as a unique display-row key. +- `seq` is the positive transcript-record sequence. One stored record can project + into more than one display row, so keep siblings with the same `id` and sequence + together. +- `kind` identifies synthetic rows. A compaction boundary uses + `kind: "compaction"` and may include `tokensBefore` and `tokensAfter` when a + matching checkpoint recorded those metrics. + +Page backward with the response's `hasMore` and `nextOffset` values. Numeric +offsets describe the current transcript projection, so do not persist them as +long-lived bookmarks across reset or compaction. Persist `__openclaw.id` instead. +To restore around a known row, call `chat.history` with `messageId` and the +`sessionId` that returned it. The Gateway can resolve that anchor from reset +archive history; anchored responses intentionally omit numeric paging metadata. + +## Subscribe instead of polling usage + +Load the initial catalog with `sessions.list`, then call `sessions.subscribe` once +per connection. Merge `sessions.changed` events by `sessionKey`. Session change +payloads can carry live `inputTokens`, `outputTokens`, `totalTokens`, +`totalTokensFresh`, `contextTokens`, `estimatedCostUsd`, response-usage settings, +and active-run state. + +Some change notifications are only invalidation signals. If an event omits the +row fields your view needs, refresh `sessions.list`. Do not poll `usage.cost` or +`sessions.usage` to keep a live session list current; reserve those methods for +on-demand aggregate or detailed reports. + +## Backfill exec approvals + +A client with `operator.approvals` should install its event listener as soon as +`hello-ok` completes, then call `exec.approval.list` to backfill requests that +predate the connection. Reconcile the list and live +`exec.approval.requested` / `exec.approval.resolved` events by approval ID so a +transition racing the list request is neither lost nor resurrected. + +## Track protocol versions + +The current wire version is `4`. General operator and WebChat clients must +negotiate the exact current version with `minProtocol: 4` and `maxProtocol: 4`. +Only authenticated node clients and lightweight probes have the N-1 acceptance +window, currently protocol `3` through `4`. + +Protocol changes are additive first. `protocol.schema.json` includes `since` +release-vintage metadata and required scope metadata for core methods, but a wire +version bump is still an explicit breaking event for third-party clients. Pin the +package versions you test, upgrade the client and Gateway together when the wire +version changes, and review the +[OpenClaw changelog](https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md) +before each upgrade. + +## Related + +- [Gateway protocol](https://docs.openclaw.ai/gateway/protocol) +- [Embedding OpenClaw](https://docs.openclaw.ai/gateway/embedding) +- [Gateway RPC reference](https://docs.openclaw.ai/reference/rpc) +- [Gateway integrations for external apps](https://docs.openclaw.ai/gateway/external-apps) diff --git a/docs/gateway/embedding.md b/docs/gateway/embedding.md new file mode 100644 index 00000000000..e430824966e --- /dev/null +++ b/docs/gateway/embedding.md @@ -0,0 +1,161 @@ +--- +summary: "Supervise the OpenClaw Gateway as a child process from Electron or another host app" +read_when: + - Embedding OpenClaw in a desktop or server application + - Supervising the Gateway as a child process + - Handling Gateway readiness, restart, shutdown, or invalid config without scraping logs +title: "Embedding OpenClaw" +--- + +An embedding host should supervise the installed `openclaw` executable, use the +Gateway WebSocket protocol as its control plane, and treat the child process as a +replaceable runtime. This keeps process ownership, readiness, failure recovery, +and upgrades explicit without depending on OpenClaw's private state layout. + +For client authentication and reconnect state, read +[Building a Gateway client](https://docs.openclaw.ai/gateway/clients). + +## Start the child with an embedding preset + +Use a real `node_modules` installation and spawn the package executable. A useful +baseline for a host that owns discovery, restart, and channel lifecycle is: + +```ts +import { spawn } from "node:child_process"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +// Supply an absolute path to a real Node runtime managed by the host application. +declare const hostNodeExecutable: string; + +const packageEntry = fileURLToPath(import.meta.resolve("openclaw")); +const openclawEntry = resolve(dirname(packageEntry), "..", "openclaw.mjs"); +const gateway = spawn(hostNodeExecutable, [openclawEntry, "gateway", "--allow-unconfigured"], { + env: { + ...process.env, + OPENCLAW_DISABLE_BONJOUR: "1", + OPENCLAW_EXEC_SHELL_SNAPSHOT: "0", + OPENCLAW_NO_RESPAWN: "1", + OPENCLAW_SKIP_CHANNELS: "1", + }, + stdio: ["ignore", "inherit", "inherit"], +}); +``` + +Resolve OpenClaw through the installed package as shown; do not assume that a +project-local `openclaw` binary is on the host process's `PATH`. The example +inherits output so the child cannot block on full stdout or stderr pipes. If the +host captures those streams instead, attach consumers immediately after spawning. + +| Setting | Embedding effect | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `OPENCLAW_DISABLE_BONJOUR=1` | Disables Gateway-owned LAN multicast advertising when the host owns discovery. | +| `OPENCLAW_NO_RESPAWN=1` | In an unmanaged embedding child, prevents OpenClaw from handing an update restart to a detached child. Routine restarts remain in process, so the host keeps ownership of the tracked PID. | +| `OPENCLAW_EXEC_SHELL_SNAPSHOT=0` | Disables login-shell snapshot capture for host exec commands. | +| `OPENCLAW_SKIP_CHANNELS=1` | Skips channel startup and reload. Set it only when the embedding app wants a control-plane or WebChat-only Gateway. | + +`--allow-unconfigured` bypasses only the `gateway.mode=local` startup guard. It +does not write configuration or repair an invalid file. Omit it when the embedding +app provisions a normal local configuration through onboarding, the config CLI, +or Gateway RPC. + +### Electron shell snapshot warning + +Shell snapshot capture runs `process.execPath -e