Skip to content

Commit 83a6bce

Browse files
authored
Update gateway connect handshake payload (#415)
- Send the newer client identity and device metadata fields - Encode device keys and signatures in the expected format - Add test coverage for the modern connect request payload
1 parent efc8b3c commit 83a6bce

3 files changed

Lines changed: 218 additions & 67 deletions

File tree

apps/server/src/openclawGatewayTest.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,32 @@ type GatewayRequestFrame = {
99
type?: unknown;
1010
id?: unknown;
1111
method?: unknown;
12+
params?: {
13+
client?: {
14+
id?: unknown;
15+
displayName?: unknown;
16+
mode?: unknown;
17+
deviceFamily?: unknown;
18+
};
19+
auth?: {
20+
password?: unknown;
21+
token?: unknown;
22+
deviceToken?: unknown;
23+
};
24+
device?: {
25+
id?: unknown;
26+
publicKey?: unknown;
27+
signature?: unknown;
28+
signedAt?: unknown;
29+
nonce?: unknown;
30+
};
31+
};
1232
};
1333

34+
function isBase64Url(value: unknown): value is string {
35+
return typeof value === "string" && /^[A-Za-z0-9_-]+$/.test(value);
36+
}
37+
1438
afterEach(async () => {
1539
await Promise.all(
1640
[...servers].map(
@@ -82,11 +106,14 @@ describe("runOpenclawGatewayTest", () => {
82106
});
83107

84108
it("passes when the modern connect handshake succeeds", async () => {
109+
let connectParams: GatewayRequestFrame["params"];
110+
85111
const gateway = await createGatewayServer((socket) => {
86112
sendChallenge(socket);
87113
socket.on("message", (data) => {
88114
const message = JSON.parse(data.toString()) as GatewayRequestFrame;
89115
if (message.type === "req" && message.method === "connect") {
116+
connectParams = message.params;
90117
socket.send(
91118
JSON.stringify({
92119
type: "res",
@@ -108,6 +135,21 @@ describe("runOpenclawGatewayTest", () => {
108135
expect(result.steps.find((step) => step.name === "WebSocket connect")?.status).toBe("pass");
109136
expect(result.steps.find((step) => step.name === "Gateway handshake")?.status).toBe("pass");
110137
expect(result.diagnostics?.observedNotifications).toContain("connect.challenge");
138+
139+
expect(connectParams?.client?.id).toBe("gateway-client");
140+
expect(connectParams?.client?.mode).toBe("backend");
141+
expect(connectParams?.client?.displayName).toBe("OK Code gateway test");
142+
expect(connectParams?.client?.deviceFamily).toBe("server");
143+
expect(connectParams?.auth?.password).toBe("topsecret");
144+
expect(connectParams?.auth?.token).toBeUndefined();
145+
expect(connectParams?.auth?.deviceToken).toBeUndefined();
146+
expect(connectParams?.device?.id).toMatch(/^[a-f0-9]{64}$/);
147+
expect(connectParams?.device?.id).not.toMatch(/^device_/);
148+
expect(isBase64Url(connectParams?.device?.publicKey)).toBe(true);
149+
expect(String(connectParams?.device?.publicKey)).not.toContain("BEGIN");
150+
expect(isBase64Url(connectParams?.device?.signature)).toBe(true);
151+
expect(connectParams?.device?.nonce).toBe("nonce-123");
152+
expect(typeof connectParams?.device?.signedAt).toBe("number");
111153
});
112154

113155
it("reports pairing-required detail codes from the connect handshake", async () => {

apps/server/src/openclawGatewayTest.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import type {
1010
TestOpenclawGatewayStepStatus,
1111
} from "@okcode/contracts";
1212
import { serverBuildInfo } from "./buildInfo.ts";
13-
import { connectOpenClawGateway } from "./provider/Layers/OpenClawGatewayClient.ts";
13+
import {
14+
OPENCLAW_GATEWAY_CLIENT_IDS,
15+
OPENCLAW_GATEWAY_CLIENT_MODES,
16+
connectOpenClawGateway,
17+
} from "./provider/Layers/OpenClawGatewayClient.ts";
1418

1519
const OPENCLAW_TEST_CONNECT_TIMEOUT_MS = 10_000;
1620
const OPENCLAW_TEST_RPC_TIMEOUT_MS = 10_000;
@@ -371,6 +375,17 @@ function buildHints(
371375
);
372376
}
373377

378+
if (
379+
errorLower.includes("/client/id") ||
380+
errorLower.includes("/client/mode") ||
381+
errorLower.includes("client id") ||
382+
errorLower.includes("client mode")
383+
) {
384+
hints.push(
385+
"The gateway rejected the advertised client identity. That usually means the gateway expects a newer OpenClaw `connect.params.client` allowlist than this OK Code build is using.",
386+
);
387+
}
388+
374389
if (
375390
diagnostics.hostKind === "tailscale" &&
376391
(detailCode === "PAIRING_REQUIRED" ||
@@ -523,15 +538,17 @@ export async function runOpenclawGatewayTest(
523538
role: "operator",
524539
scopes: [...OPENCLAW_OPERATOR_SCOPES],
525540
client: {
526-
id: "okcode",
541+
id: OPENCLAW_GATEWAY_CLIENT_IDS.GATEWAY_CLIENT,
542+
displayName: "OK Code gateway test",
527543
version: serverBuildInfo.version,
528544
platform:
529545
process.platform === "darwin"
530546
? "macos"
531547
: process.platform === "win32"
532548
? "windows"
533549
: process.platform,
534-
mode: "operator",
550+
deviceFamily: "server",
551+
mode: OPENCLAW_GATEWAY_CLIENT_MODES.BACKEND,
535552
},
536553
userAgent: `okcode/${serverBuildInfo.version}`,
537554
locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",

0 commit comments

Comments
 (0)