게이트웨이 프로토콜 (WebSocket)

게이트웨이 WS 프로토콜은 OpenClaw의 단일 제어 평면 + 노드 전송 계층입니다. 모든 클라이언트(CLI, 웹 UI, macOS 앱, iOS/Android 노드, 헤드리스 노드)가 WebSocket으로 연결하고 핸드셰이크 시 역할 + 범위를 선언합니다.

전송 계층

  • WebSocket, JSON 페이로드의 텍스트 프레임.
  • 첫 번째 프레임은 반드시 connect 요청이어야 합니다.

핸드셰이크 (connect)

게이트웨이 → 클라이언트 (사전 연결 챌린지):

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

클라이언트 → 게이트웨이:

{
  "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": "…"
    }
  }
}

게이트웨이 → 클라이언트:

{
  "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: invoke용 명령 허용 목록.
  • permissions: 세부 토글 (예: screen.record, camera.capture).

게이트웨이는 이를 클레임으로 취급하고 서버 측 허용 목록을 적용합니다.

프레즌스

  • system-presence는 디바이스 ID를 키로 하는 항목을 반환합니다.
  • 프레즌스 항목에는 deviceId, roles, scopes가 포함되어 있어 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.requestsystemRunPlan (정규 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이 일치해야 하며 그렇지 않으면 소켓이 닫힙니다.
  • 페어링 후 게이트웨이가 연결 역할 + 범위로 범위가 지정된 디바이스 토큰을 발급합니다. 이 토큰은 hello-ok.auth.deviceToken에 반환되며, 향후 연결을 위해 클라이언트가 저장해야 합니다.
  • 디바이스 토큰은 device.token.rotatedevice.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에 대한 클라이언트 동작:
    • 신뢰할 수 있는 클라이언트는 캐시된 디바이스별 토큰으로 한 번의 제한된 재시도를 시도할 수 있습니다.
    • 재시도마저 실패하면, 클라이언트는 자동 재연결 루프를 중지하고 운영자 조치 안내를 표시해야 합니다.

디바이스 ID + 페어링

  • 노드는 키쌍 핑거프린트에서 파생된 안정적인 디바이스 ID(device.id)를 포함해야 합니다.
  • 게이트웨이는 디바이스 + 역할별로 토큰을 발급합니다.
  • 로컬 자동 승인이 활성화되지 않은 한, 새 디바이스 ID에는 페어링 승인이 필요합니다.
  • 로컬 연결에는 루프백과 게이트웨이 호스트 자체의 Tailnet 주소가 포함됩니다 (따라서 동일 호스트 Tailnet 바인딩도 자동 승인 가능).
  • 모든 WS 클라이언트는 connectdevice ID를 포함해야 합니다 (operator + node). Control UI는 다음 모드에서만 생략할 수 있습니다:
    • gateway.controlUi.allowInsecureAuth=true - 로컬호스트 전용 비보안 HTTP 호환.
    • gateway.controlUi.dangerouslyDisableDeviceAuth=true (긴급 조치, 심각한 보안 수준 하락).
  • 모든 연결은 서버가 제공한 connect.challenge nonce에 서명해야 합니다.

디바이스 인증 마이그레이션 진단

챌린지 서명 이전 동작을 사용하는 레거시 클라이언트를 위해, connect는 이제 error.details.codeDEVICE_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 페이로드에 서명하세요.
  • 동일한 nonce를 connect.params.device.nonce에 전송하세요.
  • 권장 서명 페이로드는 v3로, device/client/role/scopes/token/nonce 필드 외에 platformdeviceFamily도 바인딩합니다.
  • 레거시 v2 서명은 호환성을 위해 계속 수신되지만, 재연결 시 페어링된 디바이스 메타데이터 고정이 여전히 명령 정책을 제어합니다.

TLS + 고정

  • WS 연결에 TLS를 지원합니다.
  • 클라이언트는 선택적으로 게이트웨이 인증서 핑거프린트를 고정할 수 있습니다 (gateway.tls 설정과 gateway.remote.tlsFingerprint 또는 CLI --tls-fingerprint 참고).

범위

이 프로토콜은 전체 게이트웨이 API(상태, 채널, 모델, 채팅, 에이전트, 세션, 노드, 승인 등)를 노출합니다. 정확한 인터페이스는 src/gateway/protocol/schema.ts의 TypeBox 스키마로 정의됩니다.