TypeBox là nguồn chân lý của protocol
Cập nhật lần cuối: 10/01/2026
TypeBox là một thư viện schema ưu tiên TypeScript. Mình dùng nó để định nghĩa Gateway WebSocket protocol (handshake, request/response, server events). Các schema này điều khiển runtime validation, JSON Schema export, và Swift codegen cho ứng dụng macOS. Một nguồn chân lý duy nhất; mọi thứ khác đều được tự động sinh ra.
Nếu các bạn muốn hiểu ngữ cảnh protocol ở mức cao hơn, hãy bắt đầu với Kiến trúc Gateway.
Mô hình tư duy (30 giây)
Mỗi message Gateway WS là một trong ba loại frame:
- Request:
{ type: "req", id, method, params } - Response:
{ type: "res", id, ok, payload | error } - Event:
{ type: "event", event, payload, seq?, stateVersion? }
Frame đầu tiên bắt buộc phải là request connect. Sau đó, client có thể gọi các method (ví dụ health, send, chat.send) và subscribe vào các event (ví dụ presence, tick, agent).
Luồng kết nối (tối thiểu):
Client Gateway
|---- req:connect -------->|
|<---- res:hello-ok --------|
|<---- event:tick ----------|
|---- req:health ---------->|
|<---- res:health ----------|
Các method + event phổ biến:
| Danh mục | Ví dụ | Ghi chú |
|---|---|---|
| Core | connect, health, status | connect phải là đầu tiên |
| Messaging | send, poll, agent, agent.wait | side-effects cần idempotencyKey |
| Chat | chat.history, chat.send, chat.abort, chat.inject | WebChat dùng những cái này |
| Sessions | sessions.list, sessions.patch, sessions.delete | quản trị session |
| Nodes | node.list, node.invoke, node.pair.* | Gateway WS + node actions |
| Events | tick, presence, agent, chat, health, shutdown | server push |
Danh sách chính thức nằm trong src/gateway/server.ts (METHODS, EVENTS).
Vị trí các schema
- Source:
src/gateway/protocol/schema.ts - Runtime validators (AJV):
src/gateway/protocol/index.ts - Server handshake + method dispatch:
src/gateway/server.ts - Node client:
src/gateway/client.ts - Generated JSON Schema:
dist/protocol.schema.json - Generated Swift models:
apps/macos/Sources/OpenClawProtocol/GatewayModels.swift
Pipeline hiện tại
pnpm protocol:gen- ghi JSON Schema (draft‑07) vào
dist/protocol.schema.json
- ghi JSON Schema (draft‑07) vào
pnpm protocol:gen:swift- sinh ra Swift gateway models
pnpm protocol:check- chạy cả hai generator và xác minh output đã được commit
Cách các schema được dùng lúc runtime
- Server side: mọi inbound frame đều được validate bằng AJV. Handshake chỉ chấp nhận request
connectcó params khớp vớiConnectParams. - Client side: JS client validate event và response frames trước khi dùng chúng.
- Method surface: Gateway quảng cáo các
methodsvàeventsđược hỗ trợ tronghello-ok.
Ví dụ các frame
Connect (message đầu tiên):
{
"type": "req",
"id": "c1",
"method": "connect",
"params": {
"minProtocol": 2,
"maxProtocol": 2,
"client": {
"id": "openclaw-macos",
"displayName": "macos",
"version": "1.0.0",
"platform": "macos 15.1",
"mode": "ui",
"instanceId": "A1B2"
}
}
}
Hello-ok response:
{
"type": "res",
"id": "c1",
"ok": true,
"payload": {
"type": "hello-ok",
"protocol": 2,
"server": { "version": "dev", "connId": "ws-1" },
"features": { "methods": ["health"], "events": ["tick"] },
"snapshot": {
"presence": [],
"health": {},
"stateVersion": { "presence": 0, "health": 0 },
"uptimeMs": 0
},
"policy": { "maxPayload": 1048576, "maxBufferedBytes": 1048576, "tickIntervalMs": 30000 }
}
}
Request + response:
{ "type": "req", "id": "r1", "method": "health" }
{ "type": "res", "id": "r1", "ok": true, "payload": { "ok": true } }
Event:
{ "type": "event", "event": "tick", "payload": { "ts": 1730000000 }, "seq": 12 }
Client tối thiểu (Node.js)
Luồng nhỏ nhất có ích: connect + health.
import { WebSocket } from "ws";
const ws = new WebSocket("ws://127.0.0.1:18789");
ws.on("open", () => {
ws.send(
JSON.stringify({
type: "req",
id: "c1",
method: "connect",
params: {
minProtocol: 3,
maxProtocol: 3,
client: {
id: "cli",
displayName: "example",
version: "dev",
platform: "node",
mode: "cli",
},
},
}),
);
});
ws.on("message", (data) => {
const msg = JSON.parse(String(data));
if (msg.type === "res" && msg.id === "c1" && msg.ok) {
ws.send(JSON.stringify({ type: "req", id: "h1", method: "health" }));
}
if (msg.type === "res" && msg.id === "h1") {
console.log("health:", msg.payload);
ws.close();
}
});
Ví dụ chi tiết: thêm method từ đầu đến cuối
Ví dụ: thêm request system.echo mới trả về { ok: true, text }.
- Schema (nguồn chân lý)
Thêm vào src/gateway/protocol/schema.ts:
export const SystemEchoParamsSchema = Type.Object(
{ text: NonEmptyString },
{ additionalProperties: false },
);
export const SystemEchoResultSchema = Type.Object(
{ ok: Type.Boolean(), text: NonEmptyString },
{ additionalProperties: false },
);
Thêm cả hai vào ProtocolSchemas và export types:
SystemEchoParams: SystemEchoParamsSchema,
SystemEchoResult: SystemEchoResultSchema,
export type SystemEchoParams = Static<typeof SystemEchoParamsSchema>;
export type SystemEchoResult = Static<typeof SystemEchoResultSchema>;
- Validation
Trong src/gateway/protocol/index.ts, export một AJV validator:
export const validateSystemEchoParams = ajv.compile<SystemEchoParams>(SystemEchoParamsSchema);
- Server behavior
Thêm handler trong src/gateway/server-methods/system.ts:
export const systemHandlers: GatewayRequestHandlers = {
"system.echo": ({ params, respond }) => {
const text = String(params.text ?? "");
respond(true, { ok: true, text });
},
};
Đăng ký nó trong src/gateway/server-methods.ts (đã merge systemHandlers rồi), sau đó thêm "system.echo" vào METHODS trong src/gateway/server.ts.
- Regenerate
pnpm protocol:check
- Tests + docs
Thêm server test trong src/gateway/server.*.test.ts và ghi chú method trong docs.
Hành vi Swift codegen
Swift generator sinh ra:
GatewayFrameenum với các casereq,res,event, vàunknown- Strongly typed payload structs/enums
- Giá trị
ErrorCodevàGATEWAY_PROTOCOL_VERSION
Các loại frame không xác định được giữ lại dưới dạng raw payloads để tương thích về sau.
Versioning + compatibility
PROTOCOL_VERSIONnằm trongsrc/gateway/protocol/schema.ts.- Client gửi
minProtocol+maxProtocol; server từ chối nếu không khớp. - Swift models giữ lại các loại frame không xác định để tránh làm hỏng các client cũ hơn.
Các pattern và quy ước của schema
- Hầu hết các object dùng
additionalProperties: falsecho strict payloads. NonEmptyStringlà mặc định cho ID và tên method/event.GatewayFramecấp cao nhất dùng discriminator trêntype.- Các method có side effects thường yêu cầu
idempotencyKeytrong params (ví dụ:send,poll,agent,chat.send).
Live schema JSON
Generated JSON Schema nằm trong repo tại dist/protocol.schema.json. File raw được publish thường có sẵn tại:
Khi các bạn thay đổi schema
- Cập nhật TypeBox schemas.
- Chạy
pnpm protocol:check. - Commit schema + Swift models đã được regenerate.