Gateway プロトコル(WebSocket)
Gateway WS プロトコルは、OpenClaw の統合されたコントロールプレーン兼ノードトランスポートです。すべてのクライアント(CLI、Web UI、macOS アプリ、iOS/Android ノード、ヘッドレスノード)が WebSocket で接続し、ハンドシェイク時にロールとスコープを宣言します。
トランスポート
- WebSocket、テキストフレームで JSON ペイロード。
- 最初のフレームは
connectリクエストでなければなりません。
ハンドシェイク(connect)
Gateway → クライアント(接続前チャレンジ):
{
"type": "event",
"event": "connect.challenge",
"payload": { "nonce": "…", "ts": 1737264000000 }
}
クライアント → Gateway:
{
"type": "req",
"id": "…",
"method": "connect",
"params": {
"minProtocol": 3,
"maxProtocol": 3,
"client": {
"id": "cli",
"version": "1.2.3",
"platform": "macos",
"mode": "operator"
},
"role": "operator",
"scopes": ["operator.read", "operator.write"],
"caps": [],
"commands": [],
"permissions": {},
"auth": { "token": "…" },
"locale": "en-US",
"userAgent": "openclaw-cli/1.2.3",
"device": {
"id": "device_fingerprint",
"publicKey": "…",
"signature": "…",
"signedAt": 1737264000000,
"nonce": "…"
}
}
}
Gateway → クライアント:
{
"type": "res",
"id": "…",
"ok": true,
"payload": { "type": "hello-ok", "protocol": 3, "policy": { "tickIntervalMs": 15000 } }
}
デバイストークンが発行された場合、hello-ok には以下も含まれます:
{
"auth": {
"deviceToken": "…",
"role": "operator",
"scopes": ["operator.read", "operator.write"]
}
}
ノードの例
{
"type": "req",
"id": "…",
"method": "connect",
"params": {
"minProtocol": 3,
"maxProtocol": 3,
"client": {
"id": "ios-node",
"version": "1.2.3",
"platform": "ios",
"mode": "node"
},
"role": "node",
"scopes": [],
"caps": ["camera", "canvas", "screen", "location", "voice"],
"commands": ["camera.snap", "canvas.navigate", "screen.record", "location.get"],
"permissions": { "camera.capture": true, "screen.record": false },
"auth": { "token": "…" },
"locale": "en-US",
"userAgent": "openclaw-ios/1.2.3",
"device": {
"id": "device_fingerprint",
"publicKey": "…",
"signature": "…",
"signedAt": 1737264000000,
"nonce": "…"
}
}
}
フレーミング
- リクエスト:
{type:"req", id, method, params} - レスポンス:
{type:"res", id, ok, payload|error} - イベント:
{type:"event", event, payload, seq?, stateVersion?}
副作用のあるメソッドには冪等性キーが必要です(スキーマを参照)。
ロールとスコープ
ロール
operator= コントロールプレーンクライアント(CLI/UI/自動化)。node= ケイパビリティホスト(カメラ/画面/キャンバス/system.run)。
スコープ(operator)
主なスコープ:
operator.readoperator.writeoperator.adminoperator.approvalsoperator.pairing
メソッドスコープは最初のゲートにすぎません。chat.send 経由で到達する一部のスラッシュコマンドは、その上にさらに厳密なコマンドレベルのチェックを適用します。たとえば、永続的な /config set や /config unset の書き込みには operator.admin が必要です。
Caps/commands/permissions(node)
ノードは接続時にケイパビリティのクレームを宣言します:
caps:上位レベルのケイパビリティカテゴリ。commands:invoke 用のコマンド許可リスト。permissions:きめ細かなトグル(例:screen.record、camera.capture)。
Gateway はこれらをクレームとして扱い、サーバーサイドの許可リストで強制します。
プレゼンス
system-presenceはデバイスアイデンティティをキーとするエントリを返します。- プレゼンスエントリには
deviceId、roles、scopesが含まれ、operator と node の両方で接続している場合でも、UI がデバイスごとに1行で表示できるようになっています。
ノードヘルパーメソッド
- ノードは
skills.binsを呼び出して、自動許可チェック用のスキル実行ファイルの現在のリストを取得できます。
オペレーターヘルパーメソッド
- オペレーターは
tools.catalog(operator.read)を呼び出して、エージェントのランタイムツールカタログを取得できます。レスポンスにはグループ化されたツールと出所メタデータが含まれます:source:coreまたはpluginpluginId:source="plugin"の場合のプラグインオーナーoptional:プラグインツールがオプションかどうか
実行承認
- 実行リクエストに承認が必要な場合、Gateway は
exec.approval.requestedをブロードキャストします。 - オペレータークライアントは
exec.approval.resolveを呼び出して解決します(operator.approvalsスコープが必要)。 host=nodeの場合、exec.approval.requestにはsystemRunPlan(正規のargv/cwd/rawCommand/セッションメタデータ)を含める必要があります。systemRunPlanが欠けているリクエストは拒否されます。
バージョニング
PROTOCOL_VERSIONはsrc/gateway/protocol/schema.tsに定義されています。- クライアントは
minProtocol+maxProtocolを送信し、サーバーが不一致を拒否します。 - スキーマとモデルは TypeBox 定義から生成されます:
pnpm protocol:genpnpm protocol:gen:swiftpnpm protocol:check
認証
OPENCLAW_GATEWAY_TOKEN(または--token)が設定されている場合、connect.params.auth.tokenが一致しなければソケットが閉じられます。- ペアリング後、Gateway は接続ロール + スコープにスコープされたデバイストークンを発行します。
hello-ok.auth.deviceTokenで返され、クライアントは次回の接続のために永続化する必要があります。 - デバイストークンは
device.token.rotateとdevice.token.revokeでローテーション/失効できます(operator.pairingスコープが必要)。 - 認証失敗には
error.details.codeとリカバリーヒントが含まれます:error.details.canRetryWithDeviceToken(boolean)error.details.recommendedNextStep(retry_with_device_token、update_auth_configuration、update_auth_credentials、wait_then_retry、review_auth_configuration)
AUTH_TOKEN_MISMATCHに対するクライアントの動作:- 信頼済みクライアントはキャッシュされたデバイストークンで1回の制限付きリトライを試みることができます。
- そのリトライも失敗した場合、自動再接続ループを停止し、オペレーターに操作ガイダンスを表示してください。
デバイスアイデンティティとペアリング
- ノードはキーペアのフィンガープリントから導出された安定したデバイスアイデンティティ(
device.id)を含める必要があります。 - Gateway はデバイス + ロールごとにトークンを発行します。
- ローカル自動承認が有効でない限り、新しいデバイス ID にはペアリング承認が必要です。
- ローカル接続にはループバックと Gateway ホスト自体の Tailnet アドレスが含まれます(同一ホスト Tailnet バインドでも自動承認が可能です)。
- すべての WS クライアントは
connect時にdeviceアイデンティティを含める必要があります(operator + node)。 コントロール UI は以下のモードでのみ省略できます:gateway.controlUi.allowInsecureAuth=true:localhost 限定の非セキュア HTTP 互換。gateway.controlUi.dangerouslyDisableDeviceAuth=true(緊急用、深刻なセキュリティ低下)。
- すべての接続はサーバー提供の
connect.challengenonce に署名する必要があります。
デバイス認証移行の診断
チャレンジ署名以前の動作を使用するレガシークライアントでは、connect が error.details.code に DEVICE_AUTH_* の詳細コードと安定した error.details.reason を返すようになりました。
よくある移行エラー:
| メッセージ | details.code | details.reason | 意味 |
|---|---|---|---|
device nonce required | DEVICE_AUTH_NONCE_REQUIRED | device-nonce-missing | クライアントが device.nonce を省略(または空)。 |
device nonce mismatch | DEVICE_AUTH_NONCE_MISMATCH | device-nonce-mismatch | 古い/誤った nonce で署名。 |
device signature invalid | DEVICE_AUTH_SIGNATURE_INVALID | device-signature | 署名ペイロードが v2 ペイロードと不一致。 |
device signature expired | DEVICE_AUTH_SIGNATURE_EXPIRED | device-signature-stale | 署名タイムスタンプが許容範囲外。 |
device identity mismatch | DEVICE_AUTH_DEVICE_ID_MISMATCH | device-id-mismatch | device.id が公開鍵フィンガープリントと不一致。 |
device public key invalid | DEVICE_AUTH_PUBLIC_KEY_INVALID | device-public-key | 公開鍵のフォーマット/正規化に失敗。 |
移行の目標:
connect.challengeを常に待機する。- サーバー nonce を含む v2 ペイロードに署名する。
- 同じチャレンジ nonce を
connect.params.device.nonceで送信する。 - 推奨署名ペイロードは
v3。デバイス/クライアント/ロール/スコープ/トークン/nonce フィールドに加え、platformとdeviceFamilyもバインドされます。 - レガシーの
v2署名は互換性のために引き続き受け付けられますが、ペアリング済みデバイスのメタデータピニングが再接続時のコマンドポリシーを制御します。
TLS + ピニング
- WS 接続では TLS がサポートされています。
- クライアントはオプションで Gateway 証明書のフィンガープリントをピニングできます(
gateway.tlsの設定およびgateway.remote.tlsFingerprintまたは CLI の--tls-fingerprintを参照)。
スコープ
このプロトコルは完全な Gateway API(ステータス、チャネル、モデル、チャット、エージェント、セッション、ノード、承認など)を公開します。正確なサーフェスは src/gateway/protocol/schema.ts の TypeBox スキーマで定義されています。