Gateway Protocol
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が必要です。
機能/コマンド/パーミッション (node)
ノードは接続時に機能の主張を宣言します:
caps: 高レベルの機能カテゴリ。commands: 呼び出しのためのコマンド許可リスト。permissions: 詳細なトグル (例:screen.record,camera.capture)。
Gatewayはこれらを主張として扱い、サーバーサイドの許可リストを強制します。
プレゼンス
system-presenceは、デバイスIDをキーとしたエントリを返します。- プレゼンスエントリには
deviceId、roles、scopesが含まれるため、UIはデバイスがoperatorとnodeの両方として接続する場合でも、デバイスごとに単一の行を表示できます。
ノードヘルパーメソッド
- ノードは
skills.binsを呼び出して、自動許可チェック用の現在のスキル実行ファイルのリストを取得できます。
オペレーターヘルパーメソッド
- オペレーターは
tools.catalog(operator.read) を呼び出して、エージェントのランタイムツールカタログを取得できます。レスポンスにはグループ化されたツールと出所メタデータが含まれます:source:coreまたはpluginpluginId:source="plugin"の場合のプラグイン所有者optional: プラグインツールがオプションかどうか
実行承認
- 実行リクエストに承認が必要な場合、ゲートウェイは
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スコープが必要) でローテーション/取り消しできます。
デバイスID + ペアリング
- ノードは、キーペアのフィンガープリントから導出された安定したデバイスID (
device.id) を含めるべきです。 - ゲートウェイはデバイス + ロールごとにトークンを発行します。
- ローカルの自動承認が有効でない限り、新しいデバイスIDにはペアリング承認が必要です。
- ローカル接続には、ループバックとゲートウェイホスト自身のTailnetアドレスが含まれます (同じホストのTailnetバインドでも自動承認できるように)。
- すべてのWSクライアントは
connect中にdeviceIDを含める必要があります (operator + node)。コントロールUIは、ブレークグラス用途のためにgateway.controlUi.dangerouslyDisableDeviceAuthが有効な場合のみ、これを省略できます。 - すべての接続は、サーバー提供の
connect.challengenonceに署名する必要があります。
デバイス認証移行診断
まだチャレンジ前署名動作を使用しているレガシークライアントのために、connectは安定したerror.details.reasonを持つerror.details.codeの下にDEVICE_AUTH_*詳細コードを返すようになりました。一般的な移行失敗:
| メッセージ | 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.tls設定とgateway.remote.tlsFingerprintまたはCLIの--tls-fingerprintを参照)。
スコープ
このプロトコルは完全なゲートウェイAPI (ステータス、チャネル、モデル、チャット、エージェント、セッション、ノード、承認など) を公開します。正確な表面はsrc/gateway/protocol/schema.tsのTypeBoxスキーマで定義されています。