Picture an ordinary operating session. The macOS menu bar says the Gateway is online. You start an agent request from the CLI. A Web UI renders tool events for the same run. A phone node contributes camera capability while Telegram continues receiving messages. These look like separate products, but they act on one runtime.
If every client opened Telegram, wrote the session database, and launched agents directly, “multiple clients” would become “multiple competing owners.” Processes would fight over provider connections. A config edit would affect only one copy. A node could accidentally expose local commands to every caller. After reconnecting, the Web UI would have no authoritative way to distinguish current state from events it missed.
The OpenClaw Gateway removes that split ownership. It is a long-running process that owns messaging surfaces, method dispatch, connection identity, event fanout, and much of runtime coordination. Clients receive control constrained by protocol and scopes, not references to internal objects.
Reading contract.By the end, you should be able to explain why a shared WebSocket server is not yet a control plane; why the first client frame must be connect; why hello-ok is both a welcome and a negotiated baseline; why operator, node, and worker are trust roles rather than UI names; how request, response, and event frames divide labor; and why an event gap is repaired by refreshing state instead of replaying an infinite event log.
Evidence boundary.This chapter stays on commit c549250. Wire shapes come from the TypeBox frame schemas; connection and broadcast behavior comes from Gateway source. The official Gateway architecture and Gateway protocol establish the public contract. Part six will go deeper on authentication, pairing, sandboxing, and elevated execution; this chapter focuses on how the control plane carries an authorization result.
1. A control plane is more than a central server
An HTTP or WebSocket server proves only that bytes arrive at one port. A control plane must answer four more questions: who may connect, what a connected identity may call, which authoritative state an operation changes, and how other observers learn about that change. Client copies stay projections only when all four answers have a common owner.
The official architecture states a strong invariant: one long-lived Gateway runs per host and is the only place that opens a WhatsApp/Baileys session. It maintains other messaging provider connections as well. Control clients and nodes enter the same WS server but declare different roles and capabilities. The Gateway's centrality therefore begins with resource ownership, not network topology.
| Surface | Gateway ownership | Client ownership |
|---|---|---|
| Messaging | Provider connection, inbound admission, outbound send, status. | Submit governed send/run requests and observe results. |
| Agent runs | Run registration, session ownership, event fanout, waiting, terminal state. | Start with an idempotency key and correlate by runId. |
| Configuration | Active config, reload/restart plan, persistence, audit. | Read or submit conflict-guarded writes according to scope. |
| Device nodes | Pairing, identity, connection state, command routing. | Declare caps/commands/permissions and execute approved work. |
2. Operator and node share transport, not authority
Operator clients include the CLI, Web UI, and macOS app. They call Gateway methods, read status, start agent runs, and handle approvals. Nodes are iOS, Android, macOS, or headless device execution surfaces. They advertise commands such as camera, screen recording, location, or canvas operations.
Both use WebSocket, but a node does not thereby gain arbitrary operator RPC access, and an operator cannot bypass device pairing to execute any node capability. Role enters the handshake. Scopes feed method and event authorization. Node caps, commands, and permissions describe a separate exposed surface. Transport reuse reduces protocol count; role separation preserves the trust boundary.
The source also supports a much narrower worker role. The protocol describes a closed allowlist over Gateway-owned loopback/SSH ingress: worker heartbeat, transcript commit, live event, and inference start/cancel, without general operator, node, or plugin dispatch. A mature control plane can share ingress infrastructure while giving different identities distinct protocol universes.
3. The first frame must be connect

A successful WebSocket upgrade establishes a duplex transport, not a trusted OpenClaw client. The server sends event: connect.challenge; its nonce and timestamp enter the device signature. The client responds with a normal request whose method is connect, carrying its protocol range, client metadata, role, scopes, authentication, and device proof.
The pre-auth branch in the message handler validates three things in order: a valid request envelope, method === "connect", and valid connect params. A perfectly shaped health request still cannot arrive before identity negotiation.
if (!client) {
const isRequestFrame = validateRequestFrame(parsed);
if (!isRequestFrame || parsed.method !== "connect" || !validateConnectParams(parsed.params)) {
setHandshakeState("failed");
// structured error, then close
}
}
This order avoids a dangerous architecture in which unauthenticated traffic enters the general router and every handler must remember authentication. OpenClaw establishes “this socket has become a client” before method dispatch. Method scopes remain a second authorization layer, but they operate on a negotiated identity.
4. hello-ok is a negotiated connection baseline
After authentication and device authorization, sendGatewayHello constructs hello-ok. It is substantially richer than “connected”:
serversupplies version and connection-specificconnId;featuresadvertises exposed methods, events, and capabilities;snapshotprovides presence, health, and state versions;authreports final role/scopes and may issue device tokens;policyadvertises frame, buffer, tick, attachment, and visibility constraints.
Policy values are not eternal SDK constants. Attachment limits can follow current Gateway configuration, so a client should re-read them on each reconnect. Feature discovery is not a reflection dump of every process helper either; it is the intentionally published protocol surface. Discovery, authorization, versioning, and startup availability have to agree.
The snapshot is the client's starting point at handshake time, not an eternal truth. Live changes arrive as events. Reconnects and detected gaps require a fresh read from the authoritative owner.
5. Method descriptors keep dispatch and policy together
A fragile server maintains three drifting lists: the router knows handlers, the authorization layer knows scopes, and discovery knows public names. OpenClaw collects name, family, scope, version, startup availability, and control-plane-write policy in CORE_GATEWAY_METHOD_SPECS.
const CORE_GATEWAY_METHOD_SPECS = [
["health", "health", "operator.read", "<=2026.7"],
["status", "health", "operator.read", "<=2026.7"],
["config.get", "config", "operator.read", "<=2026.7"],
["config.patch", "config", "operator.admin", "<=2026.7", { controlPlaneWrite: true }],
// ...
] as const;
Registry construction verifies that every core handler has a descriptor, preventing an unclassified handler from bypassing shared scope and write controls. Plugin methods become scoped descriptors too. Legacy handler-only plugin registries default conservatively to admin instead of being silently exposed. Compatibility should preserve callability without weakening unknown authority.
6. req, res, and event encode different relationships
After the handshake, the wire has three top-level frame kinds. The schemas use type as a discriminator:
| Frame | Correlation | Meaning |
|---|---|---|
req | id and method | The client asks the Gateway to read or perform an operation. |
res | Reuses request id | The RPC returns payload or a structured code/details error. |
event | event plus optional seq/stateVersion | The server publishes a change or progress outside request/response waiting. |
An RPC response must not be confused with long-running task completion. An agent request can return accepted with a runId; assistant, tool, and lifecycle progress then arrive through agent events. A caller that needs terminal completion uses the run/wait protocol instead of pinning the WebSocket request handler to an entire agent turn.
This separation lets request timeout, connection loss, and run continuity remain distinct. A CLI exit or HTTP handler return should not automatically cancel root work the Gateway has already admitted.
7. Broadcast authorization is per receiver
The Gateway knowing an event does not mean every connection may observe it. EVENT_SCOPE_GUARDS classifies agent, chat, cron, approval, pairing, terminal, and other streams. Broadcast also filters role, session subscription, target connection ids, and agent/session visibility.
Passive delivery needs authorization just as much as active reads. A pairing-scoped client may participate in device pairing but must not hear chat transcripts. A node must not inherit operator session broadcasts. For session-scoped clients, the subscription registry is authoritative rather than a best-effort UI preference.
The broadcaster also keeps per-client sequence numbers. After authorization filtering, clients receive different event sets; one global number would make legitimate scope filtering resemble packet loss. A sequence belongs to a delivery relationship, not to an event's universal identity.
8. Events are live notices, not a durable log

The architecture contract is explicit: events are not replayed; clients refresh on gaps. The figure uses the real read method status as one refresh example. A specific domain may instead call health, config.get, or a session query. The principle is more important than a universal state API: events say that something changed; read RPCs say what is true now.
stateVersion helps compare versioned presence or health state, but it is not a general event-store offset. Full audit or transcript recovery comes from its durable owner, not from a WS buffer. Separating live observation from durable truth makes disconnect behavior explicit.
9. Slow consumers cannot hold the Gateway hostage
A background tab that stops reading can turn unlimited WebSocket buffering into a process-wide memory problem. The broadcast loop checks each socket's bufferedAmount. Low-value events marked dropIfSlow can be skipped; other excessive buffers cause a slow-consumer close.
A skipped normal broadcast still advances that client's sequence. The client detects a gap and refreshes instead of believing its state is continuous. Backpressure, sequence detection, and snapshot repair form one design. Implementing only the first silently converts resource protection into stale UI state.
10. Shared HTTP process does not imply shared authority
The Gateway HTTP server also hosts the Control UI, canvas/A2UI, webhooks, OpenAI-compatible endpoints, and plugin surfaces. Sharing a process and port lets them use the same owner and configuration; it does not grant every route the same authentication. Source resolves auth, origin, host, plugin descriptors, and content policy by surface.
The Gateway's scoped AGENTS.md adds a performance guardrail: a static plugin-owned descriptor should be resolved without materializing the full bundled plugin runtime. Control planes sit on connection, probe, and UI hot paths. Concentrated ownership should not become concentrated eager loading.
11. Connection, RPC, and run failures are different boundaries
- Handshake failure: protocol, auth, device, or scope negotiation fails; the general dispatcher never runs.
- RPC validation/authorization failure: the connection can remain valid; the response carries structured failure details.
- Startup unavailable: unfinished sidecars can produce retryable
UNAVAILABLEwithretryAfterMs. - Agent run failure: the initial RPC may already be accepted; lifecycle is observed through the run protocol.
- Slow consumer: one projection disconnects; durable Gateway state does not roll back.
- Gateway restart: live events disappear; clients reconnect, handshake, and refresh while durable run recovery belongs to part eight.
Calling every one of these “Gateway offline” creates harmful recovery loops: credentials retry forever, accepted runs are duplicated after client timeout, and stale UI state survives reconnect. Recovery begins by identifying whether failure belongs to the connection, the RPC, the run, or the projection.
12. Six control-plane rules to carry forward
- Centralize ownership, not merely ports.Provider connections, authoritative mutations, and run admission need a single owner.
- Connection identity precedes dispatch.Negotiate protocol, role, scopes, and device before method routing.
- Discovery and authorization share a source.Method names, scopes, startup policy, and write policy cannot drift.
- Separate RPC acceptance from asynchronous completion.Request ids correlate RPCs; run ids and events correlate work.
- Events are hints; durable owners are truth.Refresh on a gap instead of assuming the live stream is an event store.
- Make backpressure observable.Sequence gaps and close reasons let clients turn resource protection into recovery.
Part three follows the route selected by this control plane: how bindings, peers, and threads produce a sessionKey; how queue modes decide whether a message joins the active run or becomes another turn; and why channel plugins cannot be allowed to rewrite session entries and transcripts arbitrarily.
Source and documentation index
- frames.ts: authoritative connect, hello-ok, req/res/event shapes.
- message-handler.ts: pre-auth first frame and authenticated dispatch seam.
- connect-hello.ts: hello-ok snapshot, features, auth, and policy.
- core-descriptors.ts: method scope, version, startup, and write policy.
- server-broadcast.ts: event scopes, session filters, per-client sequence, and backpressure.
- Gateway architecture and Gateway protocol: public architecture and wire contract.
