On this page
On this page
Schemas and formatting
TypeBox
TypeBox is a TypeScript-first schema library. OpenClaw uses it to define the Gateway WebSocket protocol (handshake, request/response, server events). Those schemas drive runtime validation (TypeBox Compile), JSON Schema export, and Swift codegen for the macOS app. One source of truth; everything else is generated.
For the higher-level protocol context, start with Gateway architecture.
Mental model (30 seconds)
Every Gateway WS message is one of three frames:
- Request:
{ type: "req", id, method, params } - Response:
{ type: "res", id, ok, payload | error } - Event:
{ type: "event", event, payload, seq?, stateVersion? }
The first frame must be a connect request. After that, clients call methods (e.g. health, send, chat.send) and subscribe to events (e.g. presence, tick, agent).
Connection flow (minimal):
Common methods and events:
| Category | Examples | Notes |
|---|---|---|
| Core | connect, health, status |
connect must be first |
| Messaging | send, agent, agent.wait, system-event, logs.tail |
side-effecting methods need idempotencyKey |
| Chat | chat.history, chat.send, chat.abort |
WebChat uses these |
| Sessions | sessions.list, sessions.patch, sessions.delete |
session admin |
| Automation | wake, cron.list, cron.run, cron.runs |
wake and cron control |
| Nodes | node.list, node.invoke, node.pair.* |
Gateway WS plus node actions |
| Events | tick, presence, agent, chat, health, shutdown |
server push |
The authoritative advertised discovery inventory lives in src/gateway/server-methods-list.ts (listGatewayMethods, GATEWAY_EVENTS).
Where the schemas live
- Source barrels:
packages/gateway-protocol/src/schema-modules.tsowns the canonical domain-module list, while the publicschema.tswrapper also exposesProtocolSchemas. - Generator selection:
packages/gateway-protocol/src/schema/protocol-schema-selection.tsderives registry names from the canonical barrel's*Schemaexports and explicitskill-library.tsimports, removing theSchemasuffix.EXCLUDED_SCHEMA_EXPORTSkeeps helpers and schemas with supplemental registry owners out of this derived selection. - Generator registry:
protocol-schemas.tscomposes the derived selection,MigrationProtocolSchemas, andSessionPlacementProtocolSchemas, rejecting duplicate keys and retaining the canonical TypeBox objects.protocol-schema-types.tspreserves each member's schema type and public readonly/writable modifiers. - Runtime validators:
packages/gateway-protocol/src/validator-registry.ts, using the lazy TypeBox Compile owner inprotocol-validator.ts - Advertised feature/discovery registry:
src/gateway/server-methods-list.ts - Server handshake and method dispatch:
src/gateway/server-core-runtime.ts - Node client:
src/gateway/client.ts - Generated JSON Schema:
dist/protocol.schema.json(build output, not committed) - Generated Swift models: SwiftPM/Xcode plugin output, or
apps/shared/OpenClawKit/.build/protocol/GatewayModels.swiftfor repository tooling
The derived selection uses lexical order of the full source export names before removing the Schema suffix. The migration and session-placement maps follow it in their own insertion order. This replaces the former manually ordered registration fragments; JSON definition order and Swift declaration placement can change without changing schema data or native declaration bodies.
Membership is now opt-out for eligible source exports: a new *Schema export in the canonical barrel enters the generator registry unless explicitly excluded. Review each new export for intended generated-protocol membership, and add helper-only exports to EXCLUDED_SCHEMA_EXPORTS. The registry guard checks selection consistency, canonical objects, exclusions, and composition; it cannot independently determine whether a newly exported schema belongs in the public generated protocol.
Current pipeline
pnpm protocol:genwrites JSON Schema (draft-07) todist/protocol.schema.json.pnpm installandpnpm buildprepare native protocol models automatically. SwiftPM/Xcode and Gradle also generate their own build-owned sources and skip unchanged generation. Fresh full-repository source archives use these same entry points.pnpm protocol:gen:swiftgenerates Swift models in the shared package's ignored.build/protocoldirectory.pnpm protocol:gen:kotlingenerates Android models and constants underapps/android/app/build/generated/openclaw-protocol.pnpm protocol:check:swiftandpnpm protocol:check:kotlinbypass the cache, check the schema/version contract, and generate twice to verify determinism.pnpm protocol:checkchecks the registry structure and all generated artifacts. Outputs are ignored build artifacts, not committed baselines. JSON Schema validation checks required frame definitions, frame ordering, thetypediscriminator mapping, and non-empty method metadata. Native validation checks protocol versions and Swift model/enum membership against the schema registry; native compatibility tests protect decoding and initializer contracts.
Commit schema and generator changes together; native outputs are never committed. scripts/native-protocol-inputs.json owns the source inputs shared by repository tooling and both native build systems. Generation checks its import graph against that manifest, so an undeclared dependency fails instead of leaving stale models. Stable decoding behavior belongs in the focused GatewayModelsCompatibilityTests.swift regressions rather than in handwritten model copies.
How the schemas are used at runtime
- Server side: every inbound frame is validated with TypeBox Compile. The handshake only accepts a
connectrequest whose params matchConnectParams. - Client side: the JS client validates event and response frames before using them.
- Feature discovery: the Gateway sends a conservative
features.methodsandfeatures.eventslist inhello-ok, fromlistGatewayMethods()andGATEWAY_EVENTS. - That discovery list is not a generated dump of every callable helper in
coreGatewayHandlers; some helper RPCs are implemented insrc/gateway/server-methods/*.tswithout being enumerated in the advertised feature list.
Example frames
Connect (first message):
Hello-ok response:
Request and response:
Event:
Minimal client (Node.js)
Smallest useful flow: connect + health.
Worked example: add a method end-to-end
Example: add a new system.echo request that returns { ok: true, text }.
- Schema (source of truth)
Add to packages/gateway-protocol/src/schema/system-info.ts (or the closest matching feature module):
system-info.ts is already exported by packages/gateway-protocol/src/schema-modules.ts, so both schemas enter ProtocolSchemas automatically as SystemEchoParams and SystemEchoResult. For a new owner module, add its export to that canonical barrel. Review any other *Schema exports it introduces and exclude helper-only schemas in protocol-schema-selection.ts. Keep schemas owned by the migration or session-placement maps in those maps and excluded from the derived selection.
Export the corresponding static types from the owner module:
- Validation
In packages/gateway-protocol/src/validator-registry.ts, export a validator using its existing lazy compiler:
- Server behavior
Add a handler in src/gateway/server-methods/system.ts:
Register it in src/gateway/server-methods.ts (already merges systemHandlers), then add "system.echo" to the listGatewayMethods input in src/gateway/server-methods-list.ts.
If the method is callable by operator or node clients, also classify it in src/gateway/method-scopes.ts so scope enforcement and hello-ok feature advertising stay aligned.
- Regenerate
- Tests and docs
Add a server test in src/gateway/server.*.test.ts and note the method in docs.
Swift codegen behavior
The Swift generator emits:
- a
GatewayFrameenum withreq,res,event, andunknowncases - strongly typed payload structs/enums
ErrorCodevalues,GATEWAY_PROTOCOL_VERSION, andGATEWAY_MIN_PROTOCOL_VERSION
Unknown frame types are preserved as raw payloads for forward compatibility.
Some registry names alias the same canonical schema object. The explicit canonical alias preferences in scripts/protocol-gen-swift.ts preserve the existing public Swift type names when registry enumeration changes. Review generated declaration bodies, including field types, initializers, and encoding/decoding behavior; moving declarations must not silently select different nominal types.
The published JSON Schema's oneOf frame order is a separate contract: req, res, then event, with the matching type discriminator mapping. scripts/lib/protocol-schema-document.mts owns and checks that order independently of registry definition order.
Versioning and compatibility
PROTOCOL_VERSIONlives inpackages/gateway-protocol/src/version.ts(current value:4).- Clients send
minProtocolandmaxProtocol; the server rejects ranges that do not include its current protocol. - The Swift models keep unknown frame types to avoid breaking older clients.
Schema patterns and conventions
- Most objects use
additionalProperties: falsefor strict payloads. NonEmptyString(Type.String({ minLength: 1 })) is the default for IDs and method/event names.- The top-level
GatewayFrameuses a discriminator ontype. - Methods with side effects usually require an
idempotencyKeyin params (example:send,poll,agent,chat.send). agentaccepts optionalinternalEventsfor runtime-generated orchestration context (for example subagent/cron task completion handoff); treat this as internal API surface.
Live schema JSON
Generated JSON Schema is a build artifact, not committed to the repo. During the package rollout, the current beta schema is available at:
When you change schemas
- Update the TypeBox schemas in the owning
packages/gateway-protocol/src/schema/*.tsmodule. Ensure a new module is exported byschema-modules.ts, review its automatic*Schemamembership, and update helper exclusions or existing supplemental owner maps as needed. - Register the method/event in
src/gateway/server-methods-list.ts. - Update
src/gateway/method-scopes.tswhen the new RPC needs operator or node scope classification. - Run
pnpm protocol:check. - Commit the regenerated Swift models.