プロトコルとAPI

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.read
  • operator.write
  • operator.admin
  • operator.approvals
  • operator.pairing

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

機能/コマンド/パーミッション (node)

ノードは接続時に機能の主張を宣言します:

  • caps: 高レベルの機能カテゴリ。
  • commands: 呼び出しのためのコマンド許可リスト。
  • permissions: 詳細なトグル (例: screen.record, camera.capture)。

Gatewayはこれらを主張として扱い、サーバーサイドの許可リストを強制します。

プレゼンス

  • system-presenceは、デバイスIDをキーとしたエントリを返します。
  • プレゼンスエントリにはdeviceIdrolesscopesが含まれるため、UIはデバイスがoperatornodeの両方として接続する場合でも、デバイスごとに単一の行を表示できます。

ノードヘルパーメソッド

  • ノードはskills.binsを呼び出して、自動許可チェック用の現在のスキル実行ファイルのリストを取得できます。

オペレーターヘルパーメソッド

  • オペレーターはtools.catalog (operator.read) を呼び出して、エージェントのランタイムツールカタログを取得できます。レスポンスにはグループ化されたツールと出所メタデータが含まれます:
    • source: core または plugin
    • pluginId: source="plugin"の場合のプラグイン所有者
    • optional: プラグインツールがオプションかどうか

実行承認

  • 実行リクエストに承認が必要な場合、ゲートウェイはexec.approval.requestedをブロードキャストします。
  • オペレータークライアントはexec.approval.resolveを呼び出して解決します (operator.approvalsスコープが必要)。
  • host=nodeの場合、exec.approval.requestにはsystemRunPlan (正規のargv/cwd/rawCommand/セッションメタデータ) を含める必要がありますsystemRunPlanが欠けているリクエストは拒否されます。

バージョニング

  • PROTOCOL_VERSIONsrc/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は接続ロール + スコープにスコープされたデバイストークンを発行します。これはhello-ok.auth.deviceTokenで返され、クライアントは将来の接続のために永続化する必要があります。
  • デバイストークンはdevice.token.rotateおよびdevice.token.revoke ( operator.pairingスコープが必要) でローテーション/取り消しできます。

デバイスID + ペアリング

  • ノードは、キーペアのフィンガープリントから導出された安定したデバイスID (device.id) を含めるべきです。
  • ゲートウェイはデバイス + ロールごとにトークンを発行します。
  • ローカルの自動承認が有効でない限り、新しいデバイスIDにはペアリング承認が必要です。
  • ローカル接続には、ループバックとゲートウェイホスト自身のTailnetアドレスが含まれます (同じホストのTailnetバインドでも自動承認できるように)。
  • すべてのWSクライアントはconnect中にdevice IDを含める必要があります (operator + node)。コントロールUIは、ブレークグラス用途のためにgateway.controlUi.dangerouslyDisableDeviceAuthが有効な場合のみ、これを省略できます。
  • すべての接続は、サーバー提供のconnect.challenge nonceに署名する必要があります。

デバイス認証移行診断

まだチャレンジ前署名動作を使用しているレガシークライアントのために、connectは安定したerror.details.reasonを持つerror.details.codeの下にDEVICE_AUTH_*詳細コードを返すようになりました。一般的な移行失敗:

メッセージ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ペイロードに署名します。
  • 同じnonceをconnect.params.device.nonceで送信します。
  • 推奨される署名ペイロードはv3で、デバイス/クライアント/ロール/スコープ/トークン/nonceフィールドに加えてplatformdeviceFamilyをバインドします。
  • レガシーv2署名は互換性のために引き続き受け入れられますが、ペアリング済みデバイスのメタデータピニングは再接続時のコマンドポリシーを制御します。

TLS + ピニング

  • WS接続でTLSがサポートされています。
  • クライアントはオプションでゲートウェイ証明書フィンガープリントをピン留めできます (gateway.tls設定とgateway.remote.tlsFingerprintまたはCLIの--tls-fingerprintを参照)。

スコープ

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

Sandbox vs Tool Policy vs ElevatedBridge Protocol