* feat(gateway): add live device scope upgrades * feat(ui): add limited-access upgrade flow * fix(protocol): refresh Swift scope upgrade models * perf(ui): lazy-load device scope upgrades * fix(ci): complete scope upgrade generated surfaces * perf(ui): lazy-load GitHub link hovercards * fix(ui): keep admin repair guidance focusable * fix(ui): gate and refresh scope upgrade banner * refactor(ui): keep gateway client within line budget * fix(ci): align rebased scope upgrade checks * fix(ui): resolve scope upgrade in browser tests * fix(gateway): honor refreshed scope upgrade deadline * fix(gateway): honor refreshed scope upgrade deadline * fix(gateway): coalesce scope upgrade waiters * fix(ui): gate scope upgrade actions * chore(plugin-sdk): refresh rebased API baseline * fix(scope-upgrade): return canonical request ids * fix(ui): preserve gateway event type binding * fix(protocol): generate scope upgrade result models * fix(ui): preserve scope upgrade recovery guidance * chore(plugin-sdk): refresh rebased API baseline * test(ui): avoid scope upgrade navigation race * docs(control-ui): clarify scope upgrade approver * test(gateway): align appended method counts * chore(plugin-sdk): refresh rebased API baseline * refactor(ui): keep place picker within line budget * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * chore(plugin-sdk): refresh rebased API baseline * fix(gateway): preserve scope-upgrade browser origin
@openclaw/gateway-client
Reference WebSocket client for the OpenClaw Gateway protocol. It provides the connection state machine used by OpenClaw's own Node and browser clients: challenge-based authentication, typed protocol frames, request correlation, timeouts, reconnect backoff, device-token handling, and event delivery.
The current wire protocol is version 4. General clients must advertise exactly v4 with
minProtocol: 4 and maxProtocol: 4. See the
Gateway protocol specification for
the complete handshake, authentication, role, scope, and method contracts.
Exact node identities (role: "node" plus mode: "node") and probe clients
can use v3. The built-in node host starts with an exact v4 envelope, then retries
an exact v3 envelope after a v3 Gateway rejects v4. If that legacy probe reaches
an upgraded v4 Gateway, the client reconnects with the full v4 envelope before
reporting readiness. Other exact node identities default to [3, 4]. Explicit
bounds override these defaults; [3, 4] on the built-in node host selects the
same bounded negotiation.
Versioning
Package versions follow the OpenClaw calendar release train: YYYY.M.PATCH,
including the OpenClaw prerelease suffix when applicable. The package version is
separate from the Gateway's current wire protocol number reported in hello-ok.
Install
npm install @openclaw/gateway-client @openclaw/gateway-protocol
Node consumers use the ws transport included as a runtime dependency. Browser
consumers provide their platform WebSocket through the browser-safe protocol
client surface.
Entry points
@openclaw/gateway-clientexports the NodeGatewayClient, device-auth helpers, readiness helpers, and timeout utilities.@openclaw/gateway-client/browserexports the browser-safe protocol client, browser device-auth lifecycle, reconnect policy, and lightweight protocol constants. Its module graph does not import Node built-ins orws.@openclaw/gateway-client/readinessexports helpers that delay client startup until the event loop can process Gateway IO.@openclaw/gateway-client/timeoutsexports timeout constants and safe timer resolution helpers.@openclaw/gateway-client/websocket-dataconverts every Nodewsraw-data shape to UTF-8 text.
Node quickstart
import { GatewayClient } from "@openclaw/gateway-client";
import { PROTOCOL_VERSION } from "@openclaw/gateway-protocol/version";
const connected = Promise.withResolvers<void>();
const client = new GatewayClient({
url: "ws://127.0.0.1:18789",
token: process.env.OPENCLAW_GATEWAY_TOKEN,
minProtocol: PROTOCOL_VERSION, // v4
maxProtocol: PROTOCOL_VERSION, // v4
onHelloOk: () => connected.resolve(),
onConnectError: (error) => connected.reject(error),
onEvent: (event) => {
console.log(event.event, event.payload);
},
});
client.start();
await connected.promise;
const status = await client.request("status", {});
console.log(status);
client.stop();
The client waits for the Gateway's connect.challenge event before sending its
connect request. It includes the challenge nonce in device authentication and
does not fall back to a pre-challenge handshake. onHelloOk fires only after the
Gateway accepts a compatible connection, so requests should wait for that callback.
For remote connections, use wss://. Plaintext ws:// is allowed by default
only for loopback addresses. Authentication material and Gateway traffic must
not cross an untrusted network without transport security.
Browser clients
Import @openclaw/gateway-client/browser when the host owns the WebSocket
adapter and device-key storage. The browser entry includes
GatewayProtocolClient and GatewayBrowserDeviceAuthLifecycle; it deliberately
omits the Node transport, TLS fingerprint handling, and private-network address
policy.
The host is responsible for:
- creating a
GatewayProtocolSocketadapter around the browser WebSocket; - loading and storing browser device identity and issued device tokens;
- signing the challenge-bound device payload;
- using the Gateway challenge
tsas the device proof'ssignedAtvalue; - supplying the client identity, role, scopes, and authentication selection;
- choosing close and reconnect behavior for product-specific errors.
The shared protocol client still owns frame parsing, request correlation, challenge ordering, timeout cleanup, sequence-gap detection, and reconnect scheduling.
Defaults and reconnect behavior
The Node client starts with a 30 second request timeout, a 15 second connect-challenge timeout, and exponential reconnect delays from 1 second to 30 seconds with a multiplier of 2. Server-provided startup retry hints may override the next delay.
The canonical defaults table and the server policy fields that can replace pre-handshake values are documented in the Gateway protocol specification.
Use the ./timeouts entry point when a host must align readiness or watchdog
budgets with these defaults. Use the ./readiness entry point when startup must
wait for an event-loop probe before opening the socket.
Bundled internals
The retry supervisor and the small @openclaw/net-policy/ip implementation are
inlined into the published JavaScript and declarations. They are implementation
details, not public exports or supported API surfaces. ipaddr.js remains an
external dependency because the inlined IP helpers use its public runtime and
types.
ws, @openclaw/gateway-protocol, and ipaddr.js remain external in the
published distribution. Consumers should import protocol types and constants
from @openclaw/gateway-protocol, not from bundled implementation paths.
Contract notes
- The client is inert at module import and construction time.
start()opens the socket;stop()closes it and rejects pending requests. - A request uses
request(method, params)afterhello-ok. PassingtimeoutMs: nullcreates an intentionally unbounded request. - Finite request deadlines reject with
GatewayProtocolRequestTimeoutError, whoseCLIENT_TIMEOUTcode, method, deadline, and send-boundary flag remain distinct from authoritative Gateway response errors. - Device identity persistence, signing, proxy routing, TLS formatting, and
logging stay host-owned through
GatewayClientHostDeps. - Protocol changes are additive first. Incompatible changes require an explicit wire-version decision and coordinated server/client follow-through.