Gateway protocol (WebSocket)
The Gateway WS protocol is the single control plane + node transport for OpenClaw. All clients (CLI, web UI, macOS app, iOS/Android nodes, headless nodes) connect over WebSocket and declare their role + scope at handshake time.Transport
- WebSocket, text frames with JSON payloads.
- First frame must be a
connectrequest.
Handshake (connect)
Gateway → Client (pre-connect challenge):hello-ok also includes:
hello-ok.auth may also include additional
bounded role entries in deviceTokens:
scopes: [] and any handed-off operator token stays bounded to the bootstrap
operator allowlist (operator.approvals, operator.read,
operator.talk.secrets, operator.write). Bootstrap scope checks stay
role-prefixed: operator entries only satisfy operator requests, and non-operator
roles still need scopes under their own role prefix.
Node example
Framing
- Request:
{type:"req", id, method, params} - Response:
{type:"res", id, ok, payload|error} - Event:
{type:"event", event, payload, seq?, stateVersion?}
Roles + scopes
Roles
operator= control plane client (CLI/UI/automation).node= capability host (camera/screen/canvas/system.run).
Scopes (operator)
Common scopes:operator.readoperator.writeoperator.adminoperator.approvalsoperator.pairingoperator.talk.secrets
talk.config with includeSecrets: true requires operator.talk.secrets
(or operator.admin).
Plugin-registered gateway RPC methods may request their own operator scope, but
reserved core admin prefixes (config.*, exec.approvals.*, wizard.*,
update.*) always resolve to operator.admin.
Method scope is only the first gate. Some slash commands reached through
chat.send apply stricter command-level checks on top. For example, persistent
/config set and /config unset writes require operator.admin.
node.pair.approve also has an extra approval-time scope check on top of the
base method scope:
- commandless requests:
operator.pairing - requests with non-exec node commands:
operator.pairing+operator.write - requests that include
system.run,system.run.prepare, orsystem.which:operator.pairing+operator.admin
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).
Presence
system-presencereturns entries keyed by device identity.- Presence entries include
deviceId,roles, andscopesso UIs can show a single row per device even when it connects as both operator and node.
Common RPC method families
This page is not a generated full dump, but the public WS surface is broader than the handshake/auth examples above. These are the main method families the Gateway exposes today.hello-ok.features.methods is a conservative discovery list built from
src/gateway/server-methods-list.ts plus loaded plugin/channel method exports.
Treat it as feature discovery, not as a generated dump of every callable helper
implemented in src/gateway/server-methods/*.ts.
System and identity
healthreturns the cached or freshly probed gateway health snapshot.statusreturns the/status-style gateway summary; sensitive fields are included only for admin-scoped operator clients.gateway.identity.getreturns the gateway device identity used by relay and pairing flows.system-presencereturns the current presence snapshot for connected operator/node devices.system-eventappends a system event and can update/broadcast presence context.last-heartbeatreturns the latest persisted heartbeat event.set-heartbeatstoggles heartbeat processing on the gateway.
Models and usage
models.listreturns the runtime-allowed model catalog.usage.statusreturns provider usage windows/remaining quota summaries.usage.costreturns aggregated cost usage summaries for a date range.doctor.memory.statusreturns vector-memory / embedding readiness for the active default agent workspace.sessions.usagereturns per-session usage summaries.sessions.usage.timeseriesreturns timeseries usage for one session.sessions.usage.logsreturns usage log entries for one session.
Channels and login helpers
channels.statusreturns built-in + bundled channel/plugin status summaries.channels.logoutlogs out a specific channel/account where the channel supports logout.web.login.startstarts a QR/web login flow for the current QR-capable web channel provider.web.login.waitwaits for that QR/web login flow to complete and starts the channel on success.push.testsends a test APNs push to a registered iOS node.voicewake.getreturns the stored wake-word triggers.voicewake.setupdates wake-word triggers and broadcasts the change.
Messaging and logs
sendis the direct outbound-delivery RPC for channel/account/thread-targeted sends outside the chat runner.logs.tailreturns the configured gateway file-log tail with cursor/limit and max-byte controls.
Talk and TTS
talk.configreturns the effective Talk config payload;includeSecretsrequiresoperator.talk.secrets(oroperator.admin).talk.modesets/broadcasts the current Talk mode state for WebChat/Control UI clients.talk.speaksynthesizes speech through the active Talk speech provider.tts.statusreturns TTS enabled state, active provider, fallback providers, and provider config state.tts.providersreturns the visible TTS provider inventory.tts.enableandtts.disabletoggle TTS prefs state.tts.setProviderupdates the preferred TTS provider.tts.convertruns one-shot text-to-speech conversion.
Secrets, config, update, and wizard
secrets.reloadre-resolves active SecretRefs and swaps runtime secret state only on full success.secrets.resolveresolves command-target secret assignments for a specific command/target set.config.getreturns the current config snapshot and hash.config.setwrites a validated config payload.config.patchmerges a partial config update.config.applyvalidates + replaces the full config payload.config.schemareturns the live config schema payload used by Control UI and CLI tooling: schema,uiHints, version, and generation metadata, including plugin + channel schema metadata when the runtime can load it. The schema includes fieldtitle/descriptionmetadata derived from the same labels and help text used by the UI, including nested object, wildcard, array-item, andanyOf/oneOf/allOfcomposition branches when matching field documentation exists.config.schema.lookupreturns a path-scoped lookup payload for one config path: normalized path, a shallow schema node, matched hint +hintPath, and immediate child summaries for UI/CLI drill-down.- Lookup schema nodes keep the user-facing docs and common validation fields:
title,description,type,enum,const,format,pattern, numeric/string/array/object bounds, and boolean flags likeadditionalProperties,deprecated,readOnly,writeOnly. - Child summaries expose
key, normalizedpath,type,required,hasChildren, plus the matchedhint/hintPath.
- Lookup schema nodes keep the user-facing docs and common validation fields:
update.runruns the gateway update flow and schedules a restart only when the update itself succeeded.wizard.start,wizard.next,wizard.status, andwizard.cancelexpose the onboarding wizard over WS RPC.
Existing major families
Agent and workspace helpers
agents.listreturns configured agent entries.agents.create,agents.update, andagents.deletemanage agent records and workspace wiring.agents.files.list,agents.files.get, andagents.files.setmanage the bootstrap workspace files exposed for an agent.agent.identity.getreturns the effective assistant identity for an agent or session.agent.waitwaits for a run to finish and returns the terminal snapshot when available.
Session control
sessions.listreturns the current session index.sessions.subscribeandsessions.unsubscribetoggle session change event subscriptions for the current WS client.sessions.messages.subscribeandsessions.messages.unsubscribetoggle transcript/message event subscriptions for one session.sessions.previewreturns bounded transcript previews for specific session keys.sessions.resolveresolves or canonicalizes a session target.sessions.createcreates a new session entry.sessions.sendsends a message into an existing session.sessions.steeris the interrupt-and-steer variant for an active session.sessions.abortaborts active work for a session.sessions.patchupdates session metadata/overrides.sessions.reset,sessions.delete, andsessions.compactperform session maintenance.sessions.getreturns the full stored session row.- chat execution still uses
chat.history,chat.send,chat.abort, andchat.inject. chat.historyis display-normalized for UI clients: inline directive tags are stripped from visible text, plain-text tool-call XML payloads (including<tool_call>...</tool_call>,<function_call>...</function_call>,<tool_calls>...</tool_calls>,<function_calls>...</function_calls>, and truncated tool-call blocks) and leaked ASCII/full-width model control tokens are stripped, pure silent-token assistant rows such as exactNO_REPLY/no_replyare omitted, and oversized rows can be replaced with placeholders.
Device pairing and device tokens
device.pair.listreturns pending and approved paired devices.device.pair.approve,device.pair.reject, anddevice.pair.removemanage device-pairing records.device.token.rotaterotates a paired device token within its approved role and scope bounds.device.token.revokerevokes a paired device token.
Node pairing, invoke, and pending work
node.pair.request,node.pair.list,node.pair.approve,node.pair.reject, andnode.pair.verifycover node pairing and bootstrap verification.node.listandnode.describereturn known/connected node state.node.renameupdates a paired node label.node.invokeforwards a command to a connected node.node.invoke.resultreturns the result for an invoke request.node.eventcarries node-originated events back into the gateway.node.canvas.capability.refreshrefreshes scoped canvas-capability tokens.node.pending.pullandnode.pending.ackare the connected-node queue APIs.node.pending.enqueueandnode.pending.drainmanage durable pending work for offline/disconnected nodes.
Approval families
exec.approval.requestandexec.approval.resolvecover one-shot exec approval requests.exec.approval.waitDecisionwaits on one pending exec approval and returns the final decision (ornullon timeout).exec.approvals.getandexec.approvals.setmanage gateway exec approval policy snapshots.exec.approvals.node.getandexec.approvals.node.setmanage node-local exec approval policy via node relay commands.plugin.approval.request,plugin.approval.waitDecision, andplugin.approval.resolvecover plugin-defined approval flows.
Other major families
- automation:
wakeschedules an immediate or next-heartbeat wake text injectioncron.list,cron.status,cron.add,cron.update,cron.remove,cron.run,cron.runs
- skills/tools:
skills.*,tools.catalog,tools.effective
Common event families
chat: UI chat updates such aschat.injectand other transcript-only chat events.session.messageandsession.tool: transcript/event-stream updates for a subscribed session.sessions.changed: session index or metadata changed.presence: system presence snapshot updates.tick: periodic keepalive / liveness event.health: gateway health snapshot update.heartbeat: heartbeat event stream update.cron: cron run/job change event.shutdown: gateway shutdown notification.node.pair.requested/node.pair.resolved: node pairing lifecycle.node.invoke.request: node invoke request broadcast.device.pair.requested/device.pair.resolved: paired-device lifecycle.voicewake.changed: wake-word trigger config changed.exec.approval.requested/exec.approval.resolved: exec approval lifecycle.plugin.approval.requested/plugin.approval.resolved: plugin approval lifecycle.
Node helper methods
- Nodes may call
skills.binsto fetch the current list of skill executables for auto-allow checks.
Operator helper methods
- Operators may call
tools.catalog(operator.read) to fetch the runtime tool catalog for an agent. The response includes grouped tools and provenance metadata:source:coreorpluginpluginId: plugin owner whensource="plugin"optional: whether a plugin tool is optional
- Operators may call
tools.effective(operator.read) to fetch the runtime-effective tool inventory for a session.sessionKeyis required.- The gateway derives trusted runtime context from the session server-side instead of accepting caller-supplied auth or delivery context.
- The response is session-scoped and reflects what the active conversation can use right now, including core, plugin, and channel tools.
- Operators may call
skills.status(operator.read) to fetch the visible skill inventory for an agent.agentIdis optional; omit it to read the default agent workspace.- The response includes eligibility, missing requirements, config checks, and sanitized install options without exposing raw secret values.
- Operators may call
skills.searchandskills.detail(operator.read) for ClawHub discovery metadata. - Operators may call
skills.install(operator.admin) in two modes:- ClawHub mode:
{ source: "clawhub", slug, version?, force? }installs a skill folder into the default agent workspaceskills/directory. - Gateway installer mode:
{ name, installId, dangerouslyForceUnsafeInstall?, timeoutMs? }runs a declaredmetadata.openclaw.installaction on the gateway host.
- ClawHub mode:
- Operators may call
skills.update(operator.admin) in two modes:- ClawHub mode updates one tracked slug or all tracked ClawHub installs in the default agent workspace.
- Config mode patches
skills.entries.<skillKey>values such asenabled,apiKey, andenv.
Exec approvals
- When an exec request needs approval, the gateway broadcasts
exec.approval.requested. - Operator clients resolve by calling
exec.approval.resolve(requiresoperator.approvalsscope). - For
host=node,exec.approval.requestmust includesystemRunPlan(canonicalargv/cwd/rawCommand/session metadata). Requests missingsystemRunPlanare rejected. - After approval, forwarded
node.invoke system.runcalls reuse that canonicalsystemRunPlanas the authoritative command/cwd/session context. - If a caller mutates
command,rawCommand,cwd,agentId, orsessionKeybetween prepare and the final approvedsystem.runforward, the gateway rejects the run instead of trusting the mutated payload.
Agent delivery fallback
agentrequests can includedeliver=trueto request outbound delivery.bestEffortDeliver=falsekeeps strict behavior: unresolved or internal-only delivery targets returnINVALID_REQUEST.bestEffortDeliver=trueallows fallback to session-only execution when no external deliverable route can be resolved (for example internal/webchat sessions or ambiguous multi-channel configs).
Versioning
PROTOCOL_VERSIONlives insrc/gateway/protocol/schema.ts.- Clients send
minProtocol+maxProtocol; the server rejects mismatches. - Schemas + models are generated from TypeBox definitions:
pnpm protocol:genpnpm protocol:gen:swiftpnpm protocol:check
Auth
- Shared-secret gateway auth uses
connect.params.auth.tokenorconnect.params.auth.password, depending on the configured auth mode. - Identity-bearing modes such as Tailscale Serve
(
gateway.auth.allowTailscale: true) or non-loopbackgateway.auth.mode: "trusted-proxy"satisfy the connect auth check from request headers instead ofconnect.params.auth.*. - Private-ingress
gateway.auth.mode: "none"skips shared-secret connect auth entirely; do not expose that mode on public/untrusted ingress. - After pairing, the Gateway issues a device token scoped to the connection
role + scopes. It is returned in
hello-ok.auth.deviceTokenand should be persisted by the client for future connects. - Clients should persist the primary
hello-ok.auth.deviceTokenafter any successful connect. - Reconnecting with that stored device token should also reuse the stored approved scope set for that token. This preserves read/probe/status access that was already granted and avoids silently collapsing reconnects to a narrower implicit admin-only scope.
- Normal connect auth precedence is explicit shared token/password first, then
explicit
deviceToken, then stored per-device token, then bootstrap token. - Additional
hello-ok.auth.deviceTokensentries are bootstrap handoff tokens. Persist them only when the connect used bootstrap auth on a trusted transport such aswss://or loopback/local pairing. - If a client supplies an explicit
deviceTokenor explicitscopes, that caller-requested scope set remains authoritative; cached scopes are only reused when the client is reusing the stored per-device token. - Device tokens can be rotated/revoked via
device.token.rotateanddevice.token.revoke(requiresoperator.pairingscope). - Token issuance/rotation stays bounded to the approved role set recorded in that device’s pairing entry; rotating a token cannot expand the device into a role that pairing approval never granted.
- For paired-device token sessions, device management is self-scoped unless the
caller also has
operator.admin: non-admin callers can remove/revoke/rotate only their own device entry. device.token.rotatealso checks the requested operator scope set against the caller’s current session scopes. Non-admin callers cannot rotate a token into a broader operator scope set than they already hold.- Auth failures include
error.details.codeplus recovery hints:error.details.canRetryWithDeviceToken(boolean)error.details.recommendedNextStep(retry_with_device_token,update_auth_configuration,update_auth_credentials,wait_then_retry,review_auth_configuration)
- Client behavior for
AUTH_TOKEN_MISMATCH:- Trusted clients may attempt one bounded retry with a cached per-device token.
- If that retry fails, clients should stop automatic reconnect loops and surface operator action guidance.
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.
- Pairing auto-approval is centered on direct local loopback connects.
- OpenClaw also has a narrow backend/container-local self-connect path for trusted shared-secret helper flows.
- Same-host tailnet or LAN connects are still treated as remote for pairing and require approval.
- All WS clients must include
deviceidentity duringconnect(operator + node). Control UI can omit it only in these modes:gateway.controlUi.allowInsecureAuth=truefor localhost-only insecure HTTP compatibility.- successful
gateway.auth.mode: "trusted-proxy"operator Control UI auth. gateway.controlUi.dangerouslyDisableDeviceAuth=true(break-glass, severe security downgrade).
- All connections must sign the server-provided
connect.challengenonce.
Device auth migration diagnostics
For legacy clients that still use pre-challenge signing behavior,connect now returns
DEVICE_AUTH_* detail codes under error.details.code with a stable error.details.reason.
Common migration failures:
| Message | details.code | details.reason | Meaning |
|---|---|---|---|
device nonce required | DEVICE_AUTH_NONCE_REQUIRED | device-nonce-missing | Client omitted device.nonce (or sent blank). |
device nonce mismatch | DEVICE_AUTH_NONCE_MISMATCH | device-nonce-mismatch | Client signed with a stale/wrong nonce. |
device signature invalid | DEVICE_AUTH_SIGNATURE_INVALID | device-signature | Signature payload does not match v2 payload. |
device signature expired | DEVICE_AUTH_SIGNATURE_EXPIRED | device-signature-stale | Signed timestamp is outside allowed skew. |
device identity mismatch | DEVICE_AUTH_DEVICE_ID_MISMATCH | device-id-mismatch | device.id does not match public key fingerprint. |
device public key invalid | DEVICE_AUTH_PUBLIC_KEY_INVALID | device-public-key | Public key format/canonicalization failed. |
- Always wait for
connect.challenge. - Sign the v2 payload that includes the server nonce.
- Send the same nonce in
connect.params.device.nonce. - Preferred signature payload is
v3, which bindsplatformanddeviceFamilyin addition to device/client/role/scopes/token/nonce fields. - Legacy
v2signatures remain accepted for compatibility, but paired-device metadata pinning still controls command policy on reconnect.
TLS + pinning
- TLS is supported for WS connections.
- Clients may optionally pin the gateway cert fingerprint (see
gateway.tlsconfig plusgateway.remote.tlsFingerprintor CLI--tls-fingerprint).
Scope
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 insrc/gateway/protocol/schema.ts.