コンテンツにスキップ

OpenClaw Gatewayプロトコル接続ガイド:WebSocket実装手順

分散システムにおいて、多数のクライアントやノードをリアルタイムで同期させ、かつセキュアに管理するのは非常に骨が折れる作業です。通信の遅延や認証の複雑さに悩まされた経験がある開発者も多いのではないでしょうか。

OpenClaw では、こうした課題を解決するために WebSocket をベースにした Gateway プロトコルを採用しています。このプロトコルは、OpenClaw の単一のコントロールプレーンおよびノード間のトランスポートとして機能します。CLI、Web UI、モバイルアプリ、各種ノードなど、すべてのクライアントは WebSocket を通じて接続し、ハンドシェイク時に自身の role(ロール)と scope(スコープ)を宣言する仕組みになっています。

  • WebSocket を使用し、JSON ペイロードを含むテキストフレームで通信します。
  • 接続後の最初のフレームは、必ず connect リクエストである必要があります。

Gateway → Client (接続前のチャレンジ):

{
"type": "event",
"event": "connect.challenge",
"payload": { "nonce": "…", "ts": 1737264000000 }
}

Client → 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 → Client:

{
"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": "…"
}
}
}
  • Request: {type:"req", id, method, params}
  • Response: {type:"res", id, ok, payload|error}
  • Event: {type:"event", event, payload, seq?, stateVersion?}

副作用を伴うメソッドには、idempotency keys(べき等キー)が必要です(詳細はスキーマを参照してください)。

  • operator: コントロールプレーンのクライアント(CLI、UI、自動化ツールなど)。
  • node: 機能をホストする側(カメラ、スクリーン、キャンバス、system.run など)。

よく使われるスコープは以下の通りです。

  • operator.read
  • operator.write
  • operator.admin
  • operator.approvals
  • operator.pairing

メソッドのスコープは最初のチェックゲートに過ぎません。chat.send 経由で実行される一部のスラッシュコマンドには、さらに厳格なコマンドレベルのチェックが適用されます。例えば、永続的な /config set や /config unset の書き込みには operator.admin が必要です。

Node は接続時に自身の機能(capability)を宣言します。

  • caps: 高レベルな機能カテゴリ。
  • commands: 実行を許可するコマンドのホワイトリスト。
  • permissions: 詳細な切り替え設定(例: screen.record, camera.capture)。

Gateway はこれらを「自己申告(claims)」として扱い、サーバー側のホワイトリストに基づいて制限を適用します。

  • system-presence は、デバイスの識別子をキーとしたエントリを返します。
  • プレゼンスエントリには deviceId、roles、scopes が含まれます。これにより、あるデバイスが operator と node の両方として接続している場合でも、UI 上では 1 つのデバイスとして表示できます。
  • Node は skills.bins を呼び出して、自動許可チェック用のスキル実行ファイルリストを取得できます。
  • Operator は tools.catalog (operator.read) を呼び出して、エージェントのランタイムツールカタログを取得できます。レスポンスにはグループ化されたツールとメタデータが含まれます。
    • source: core または plugin
    • pluginId: source="plugin" の場合のプラグイン所有者
    • optional: プラグインツールがオプションかどうか
  • Operator は tools.effective (operator.read) を呼び出して、セッションで有効なツールのインベントリを取得できます。
    • sessionKey が必須です。
    • Gateway は、呼び出し元から提供された認証情報ではなく、サーバー側のセッションから信頼できる実行コンテキストを導き出します。
    • レスポンスはセッションスコープであり、コアツール、プラグイン、チャネルツールを含め、現在のアクティブな会話で使用可能なものが反映されます。
  • 実行リクエストに承認が必要な場合、Gateway は exec.approval.requested をブロードキャストします。
  • Operator クライアントは exec.approval.resolve を呼び出してこれを解決します(operator.approvals スコアが必要です)。
  • host=node の場合、exec.approval.request には systemRunPlan(標準的な argv, cwd, rawCommand, セッションメタデータ)を含める必要があります。systemRunPlan がないリクエストは拒否されます。

エージェント配信のフォールバック

Section titled “エージェント配信のフォールバック”
  • agent リクエストに deliver=true を含めることで、外部への配信をリクエストできます。
  • bestEffortDeliver=false の場合、厳格な挙動となります。解決できない配信先や内部専用の配信先を指定すると INVALID_REQUEST が返されます。
  • bestEffortDeliver=true の場合、外部への配信ルートが解決できないとき(内部セッションや Web チャット、曖昧なマルチチャネル設定など)に、セッション内での実行にフォールバックすることを許可します。
  • PROTOCOL_VERSION は src/gateway/protocol/schema.ts で定義されています。
  • クライアントは minProtocol と maxProtocol を送信し、サーバー側で不一致がある場合は接続を拒否します。
  • スキーマとモデルは TypeBox の定義から生成されます。
    • pnpm protocol:gen
    • pnpm protocol:gen:swift
    • pnpm protocol:check
  • OPENCLAW_GATEWAY_TOKEN(または --token)が設定されている場合、connect.params.auth.token が一致しないとソケットは閉じられます。
  • ペアリング後、Gateway は接続ロールとスコープに応じた device token を発行します。これは 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 回だけ再試行を試みることができます。
    • 再試行に失敗した場合は、自動再接続ループを停止し、ユーザーに適切なアクションを促す必要があります。
  • Node は、キーペアのフィンガープリントから派生した固定のデバイス ID (device.id) を含める必要があります。
  • Gateway はデバイスとロールの組み合わせごとにトークンを発行します。
  • 新しいデバイス ID の場合、ローカルの自動承認が有効でない限り、ペアリングの承認が必要です。
  • ローカル接続には、ループバックアドレスおよび Gateway ホスト自身の Tailscale アドレスが含まれます(同一ホストの Tailscale 経由であれば自動承認が可能です)。
  • すべての WebSocket クライアントは、connect 時に device ID を含める必要があります。コントロール UI でこれを省略できるのは、以下のモードに限られます。
    • gateway.controlUi.allowInsecureAuth=true: localhost 限定の非セキュアな HTTP 互換用。
    • gateway.controlUi.dangerouslyDisableDeviceAuth=true: 緊急用(セキュリティレベルが著しく低下します)。
  • すべての接続において、サーバーから提供された connect.challenge の nonce に対して署名を行う必要があります。

チャレンジ前の署名方式を使用している古いクライアントのために、connect は error.details.code に DEVICE_AUTH_* コードを返し、error.details.reason に詳細な理由を含めます。

よくある移行時のエラー:

メッセージdetails.codedetails.reason意味
device nonce requiredDEVICE_AUTH_NONCE_REQUIREDdevice-nonce-missingクライアントが device.nonce を省略した(または空で送った)。
device nonce mismatchDEVICE_AUTH_NONCE_MISMATCHdevice-nonce-mismatchクライアントが古い、または間違った nonce で署名した。
device signature invalidDEVICE_AUTH_SIGNATURE_INVALIDdevice-signature署名のペイロードが v2 の形式と一致しない。
device signature expiredDEVICE_AUTH_SIGNATURE_EXPIREDdevice-signature-stale署名されたタイムスタンプが許容範囲外。
device identity mismatchDEVICE_AUTH_DEVICE_ID_MISMATCHdevice-id-mismatchdevice.id が公開鍵のフィンガープリントと一致しない。
device public key invalidDEVICE_AUTH_PUBLIC_KEY_INVALIDdevice-public-key公開鍵のフォーマットまたは正規化に失敗した。

移行のポイント:

  • 常に connect.challenge を待機してください。
  • サーバーの nonce を含む v2 ペイロードに署名してください。
  • connect.params.device.nonce に同じ nonce を含めて送信してください。
  • 推奨される署名ペイロードは v3 です。これはデバイス、クライアント、ロール、スコープ、トークン、nonce に加え、platform と deviceFamily も紐付けます。
  • 互換性のためにレガシーな v2 署名も受け入れられますが、再接続時のコマンドポリシーはペアリングされたデバイスのメタデータによって制御されます。
  • WebSocket 接続での TLS をサポートしています。
  • クライアントは、オプションで Gateway 証明書のフィンガープリントをピン留め(Pinning)できます(gateway.tls 設定、または gateway.remote.tlsFingerprint / CLI の --tls-fingerprint を参照してください)。

このプロトコルは、Gateway API の全機能(ステータス、チャネル、モデル、チャット、エージェント、セッション、ノード、承認など)を公開します。具体的なインターフェースは src/gateway/protocol/schema.ts 内の TypeBox スキーマで定義されています。

AI Setup Assistant

OpenClaw

OpenClaw Expert

まだ解決しませんか?

このページで解決しない場合は、OpenClaw Expertに直接質問してください。