Gateway
Gateway 프로토콜
Gateway WS 프로토콜은 OpenClaw의 단일 제어 플레인 + 노드 전송입니다. 모든 클라이언트(CLI, 웹 UI, macOS 앱, iOS/Android 노드, 헤드리스 노드)는 WebSocket으로 연결하고 핸드셰이크 시점에 자신의 역할 + 범위를 선언합니다.
전송
- WebSocket, JSON 페이로드가 있는 텍스트 프레임.
- 첫 번째 프레임은 반드시
connect요청이어야 합니다. - 연결 전 프레임은 64 KiB로 제한됩니다. 핸드셰이크가 성공한 뒤에는 클라이언트가
hello-ok.policy.maxPayload및hello-ok.policy.maxBufferedBytes제한을 따라야 합니다. 진단이 활성화된 경우, 크기가 초과된 인바운드 프레임과 느린 아웃바운드 버퍼는 Gateway가 영향을 받은 프레임을 닫거나 삭제하기 전에payload.large이벤트를 내보냅니다. 이러한 이벤트는 크기, 제한, 표면, 안전한 이유 코드를 보관합니다. 메시지 본문, 첨부 파일 내용, 원시 프레임 본문, 토큰, 쿠키 또는 비밀 값은 보관하지 않습니다.
핸드셰이크(connect)
Gateway → 클라이언트(연결 전 챌린지):
{ "type": "event", "event": "connect.challenge", "payload": { "nonce": "…", "ts": 1737264000000 }}클라이언트 → Gateway:
{ "type": "req", "id": "…", "method": "connect", "params": { "minProtocol": 3, "maxProtocol": 4, "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": 4, "server": { "version": "…", "connId": "…" }, "features": { "methods": ["…"], "events": ["…"] }, "snapshot": { "…": "…" }, "auth": { "role": "operator", "scopes": ["operator.read", "operator.write"] }, "policy": { "maxPayload": 26214400, "maxBufferedBytes": 52428800, "tickIntervalMs": 15000 } }}Gateway가 아직 시작 사이드카를 마무리하는 중일 때는 connect 요청이 details.reason이 "startup-sidecars"로 설정되고 retryAfterMs가 포함된 재시도 가능한 UNAVAILABLE 오류를 반환할 수 있습니다. 클라이언트는 이를 최종 핸드셰이크 실패로 표시하지 말고 전체 연결 예산 내에서 해당 응답을 재시도해야 합니다.
server, features, snapshot, policy는 모두 스키마(packages/gateway-protocol/src/schema/frames.ts)에서 필수입니다. auth도 필수이며 협상된 역할/범위를 보고합니다. pluginSurfaceUrls는 선택 사항이며 canvas 같은 Plugin 표면 이름을 범위가 지정된 호스팅 URL에 매핑합니다.
범위가 지정된 Plugin 표면 URL은 만료될 수 있습니다. 노드는 node.pluginSurface.refresh를 { "surface": "canvas" }와 함께 호출하여 pluginSurfaceUrls의 새 항목을 받을 수 있습니다. 실험적 Canvas Plugin 리팩터는 더 이상 사용되지 않는 canvasHostUrl, canvasCapability, node.canvas.capability.refresh 호환 경로를 지원하지 않습니다. 현재 네이티브 클라이언트와 Gateway는 Plugin 표면을 사용해야 합니다.
디바이스 토큰이 발급되지 않으면 hello-ok.auth는 토큰 필드 없이 협상된 권한을 보고합니다.
{ "auth": { "role": "operator", "scopes": ["operator.read", "operator.write"] }}신뢰할 수 있는 동일 프로세스 백엔드 클라이언트(client.id: "gateway-client", client.mode: "backend")는 공유 Gateway 토큰/비밀번호로 인증하는 직접 루프백 연결에서 device를 생략할 수 있습니다. 이 경로는 내부 제어 플레인 RPC용으로 예약되어 있으며, 오래된 CLI/디바이스 페어링 기준선이 서브에이전트 세션 업데이트 같은 로컬 백엔드 작업을 차단하지 않도록 합니다. 원격 클라이언트, 브라우저 출처 클라이언트, 노드 클라이언트, 명시적 디바이스 토큰/디바이스 ID 클라이언트는 여전히 일반적인 페어링 및 범위 업그레이드 검사를 사용합니다.
디바이스 토큰이 발급되면 hello-ok에는 다음도 포함됩니다.
{ "auth": { "deviceToken": "…", "role": "operator", "scopes": ["operator.read", "operator.write"] }}기본 제공 QR/설정 코드 부트스트랩은 새로운 모바일 핸드오프 경로입니다. 성공적인 기준 설정 코드 연결은 기본 노드 토큰과 제한된 운영자 토큰 하나를 반환합니다.
{ "auth": { "deviceToken": "…", "role": "node", "scopes": [], "deviceTokens": [ { "deviceToken": "…", "role": "operator", "scopes": ["operator.approvals", "operator.read", "operator.talk.secrets", "operator.write"] } ] }}운영자 핸드오프는 QR 온보딩이 페어링 변경 범위나 operator.admin을 부여하지 않고 모바일 운영자 루프를 시작하고 네이티브 설정을 완료할 수 있도록 의도적으로 제한됩니다. 여기에는 네이티브 클라이언트가 부트스트랩 이후 필요한 Talk 구성을 읽을 수 있도록 operator.talk.secrets가 포함됩니다. 더 넓은 페어링 및 관리자 액세스에는 별도의 승인된 운영자 페어링 또는 토큰 흐름이 필요합니다. 클라이언트는 신뢰할 수 있는 전송(예: wss:// 또는 루프백/로컬 페어링)에서 부트스트랩 인증으로 연결한 경우에만 hello-ok.auth.deviceTokens를 유지해야 합니다.
노드 예시
{ "type": "req", "id": "…", "method": "connect", "params": { "minProtocol": 3, "maxProtocol": 4, "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= 기능 호스트(camera/screen/canvas/system.run).
범위(운영자)
일반 범위:
operator.readoperator.writeoperator.adminoperator.approvalsoperator.pairingoperator.talk.secrets
includeSecrets: true가 포함된 talk.config에는 operator.talk.secrets(또는 operator.admin)가 필요합니다.
비밀이 포함된 경우, 클라이언트는 활성 Talk 제공자 자격 증명을 talk.resolved.config.apiKey에서 읽어야 합니다. talk.providers.<id>.apiKey는 소스 형태로 유지되며 SecretRef 객체 또는 편집된 문자열일 수 있습니다.
Plugin이 등록한 Gateway RPC 메서드는 자체 운영자 범위를 요청할 수 있지만, 예약된 핵심 관리자 접두사(config.*, exec.approvals.*, wizard.*, update.*)는 항상 operator.admin으로 해석됩니다.
메서드 범위는 첫 번째 관문일 뿐입니다. chat.send를 통해 도달한 일부 슬래시 명령은 그 위에 더 엄격한 명령 수준 검사를 적용합니다. 예를 들어, 영구적인 /config set 및 /config unset 쓰기에는 operator.admin이 필요합니다.
node.pair.approve에도 기본 메서드 범위에 더해 승인 시 추가 범위 검사가 있습니다.
- 명령이 없는 요청:
operator.pairing - exec이 아닌 노드 명령이 있는 요청:
operator.pairing+operator.write system.run,system.run.prepare또는system.which가 포함된 요청:operator.pairing+operator.admin
기능/명령/권한(노드)
노드는 연결 시 기능 클레임을 선언합니다.
caps:camera,canvas,screen,location,voice,talk같은 상위 수준 기능 범주.commands: invoke용 명령 허용 목록.permissions: 세분화된 토글(예:screen.record,camera.capture).
Gateway는 이를 클레임으로 취급하고 서버 측 허용 목록을 적용합니다.
존재 상태
system-presence는 디바이스 ID를 키로 하는 항목을 반환합니다.- 존재 상태 항목에는
deviceId,roles,scopes가 포함되어 UI가 운영자와 노드 둘 다로 연결된 경우에도 디바이스당 단일 행을 표시할 수 있습니다. node.list에는 선택적lastSeenAtMs및lastSeenReason필드가 포함됩니다. 연결된 노드는 현재 연결 시간을 이유connect와 함께lastSeenAtMs로 보고합니다. 페어링된 노드는 신뢰할 수 있는 노드 이벤트가 페어링 메타데이터를 업데이트할 때 지속적인 백그라운드 존재 상태도 보고할 수 있습니다.
노드 백그라운드 생존 이벤트
노드는 event: "node.presence.alive"와 함께 node.event를 호출하여 페어링된 노드가 백그라운드 깨우기 중에 연결된 것으로 표시하지 않고도 살아 있었음을 기록할 수 있습니다.
{ "event": "node.presence.alive", "payloadJSON": "{\"trigger\":\"silent_push\",\"sentAtMs\":1737264000000,\"displayName\":\"Peter's iPhone\",\"version\":\"2026.4.28\",\"platform\":\"iOS 18.4.0\",\"deviceFamily\":\"iPhone\",\"modelIdentifier\":\"iPhone17,1\",\"pushTransport\":\"relay\"}"}trigger는 닫힌 열거형입니다: background, silent_push, bg_app_refresh, significant_location, manual 또는 connect. 알 수 없는 트리거 문자열은 지속 전에 Gateway에 의해 background로 정규화됩니다. 이 이벤트는 인증된 노드 디바이스 세션에 대해서만 지속됩니다. 디바이스가 없거나 페어링되지 않은 세션은 handled: false를 반환합니다.
성공한 Gateway는 구조화된 결과를 반환합니다.
{ "ok": true, "event": "node.presence.alive", "handled": true, "reason": "persisted"}이전 Gateway는 node.event에 대해 여전히 { "ok": true }를 반환할 수 있습니다. 클라이언트는 이를 지속적인 존재 상태 저장이 아니라 승인된 RPC로 취급해야 합니다.
브로드캐스트 이벤트 범위 지정
서버가 푸시하는 WebSocket 브로드캐스트 이벤트는 페어링 범위 세션이나 노드 전용 세션이 세션 콘텐츠를 수동으로 수신하지 않도록 범위로 제한됩니다.
- 채팅, 에이전트 및 도구 결과 프레임(스트리밍된
agent이벤트와 도구 호출 결과 포함)에는 최소operator.read가 필요합니다.operator.read가 없는 세션은 이러한 프레임을 완전히 건너뜁니다. - Plugin 정의
plugin.*브로드캐스트는 Plugin이 등록한 방식에 따라operator.write또는operator.admin으로 제한됩니다. - 상태 및 전송 이벤트(
heartbeat,presence,tick, 연결/해제 수명 주기 등)는 모든 인증된 세션이 전송 상태를 관찰할 수 있도록 제한되지 않습니다. - 알 수 없는 브로드캐스트 이벤트 패밀리는 등록된 핸들러가 명시적으로 완화하지 않는 한 기본적으로 범위로 제한됩니다(폐쇄형 실패).
각 클라이언트 연결은 자체 클라이언트별 시퀀스 번호를 유지하므로, 서로 다른 클라이언트가 이벤트 스트림의 서로 다른 범위 필터링 부분집합을 보더라도 브로드캐스트는 해당 소켓에서 단조 순서를 보존합니다.
일반 RPC 메서드 패밀리
공개 WS 표면은 위의 핸드셰이크/인증 예시보다 더 넓습니다. 이는 생성된 덤프가 아닙니다. hello-ok.features.methods는 src/gateway/server-methods-list.ts와 로드된 Plugin/채널 메서드 export로부터 빌드된 보수적인 탐색 목록입니다. 이를 기능 탐색으로 취급하고, src/gateway/server-methods/*.ts의 전체 열거로 취급하지 마세요.
시스템 및 ID
health는 캐시된 Gateway 상태 스냅샷 또는 새로 프로브한 Gateway 상태 스냅샷을 반환합니다.diagnostics.stability는 최근의 제한된 진단 안정성 기록기를 반환합니다. 이벤트 이름, 개수, 바이트 크기, 메모리 측정값, 큐/세션 상태, 채널/Plugin 이름, 세션 ID 같은 운영 메타데이터를 보관합니다. 채팅 텍스트, Webhook 본문, 도구 출력, 원시 요청 또는 응답 본문, 토큰, 쿠키, 비밀 값은 보관하지 않습니다. 운영자 읽기 범위가 필요합니다.status는/status형식의 Gateway 요약을 반환합니다. 민감한 필드는 관리자 범위 운영자 클라이언트에만 포함됩니다.gateway.identity.get은 릴레이 및 페어링 흐름에서 사용하는 Gateway 장치 ID를 반환합니다.system-presence는 연결된 운영자/Node 장치의 현재 프레즌스 스냅샷을 반환합니다.system-event는 시스템 이벤트를 추가하고 프레즌스 컨텍스트를 업데이트/브로드캐스트할 수 있습니다.last-heartbeat는 최근에 지속 저장된 Heartbeat 이벤트를 반환합니다.set-heartbeats는 Gateway의 Heartbeat 처리를 전환합니다.
모델 및 사용량
models.list는 런타임에서 허용되는 모델 카탈로그를 반환합니다. 선택기에 맞춘 구성된 모델에는{ "view": "configured" }를 전달하고(agents.defaults.models가 먼저, 그다음models.providers.*.models), 전체 카탈로그에는{ "view": "all" }을 전달합니다.usage.status는 공급자 사용량 윈도우/남은 할당량 요약을 반환합니다.usage.cost는 날짜 범위의 집계된 비용 사용량 요약을 반환합니다. 한 에이전트에는agentId를 전달하고, 구성된 에이전트를 집계하려면agentScope: "all"을 전달합니다.doctor.memory.status는 활성 기본 에이전트 작업 영역의 벡터 메모리 / 캐시된 임베딩 준비 상태를 반환합니다. 호출자가 명시적으로 실시간 임베딩 공급자 핑을 원하는 경우에만{ "probe": true }또는{ "deep": true }를 전달합니다. Dreaming 인식 클라이언트는 선택한 에이전트 작업 영역으로 Dreaming 저장소 통계를 범위 지정하기 위해{ "agentId": "agent-id" }도 전달할 수 있습니다.agentId를 생략하면 기본 에이전트 폴백을 유지하고 구성된 Dreaming 작업 영역을 집계합니다.doctor.memory.dreamDiary,doctor.memory.backfillDreamDiary,doctor.memory.resetDreamDiary,doctor.memory.resetGroundedShortTerm,doctor.memory.repairDreamingArtifacts,doctor.memory.dedupeDreamDiary는 선택한 에이전트 Dreaming 보기/작업을 위해 선택적{ "agentId": "agent-id" }매개변수를 받습니다.agentId를 생략하면 구성된 기본 에이전트 작업 영역에서 작동합니다.doctor.memory.remHarness는 원격 제어 플레인 클라이언트를 위한 제한된 읽기 전용 REM 하네스 미리보기를 반환합니다. 작업 영역 경로, 메모리 스니펫, 렌더링된 grounded markdown, 심층 승격 후보를 포함할 수 있으므로 호출자에게operator.read가 필요합니다.sessions.usage는 세션별 사용량 요약을 반환합니다. 한 에이전트에는agentId를 전달하고, 구성된 에이전트를 함께 나열하려면agentScope: "all"을 전달합니다.sessions.usage.timeseries는 한 세션의 시계열 사용량을 반환합니다.sessions.usage.logs는 한 세션의 사용량 로그 항목을 반환합니다.
채널 및 로그인 헬퍼
channels.status는 기본 제공 + 번들 채널/Plugin 상태 요약을 반환합니다.channels.logout는 채널이 로그아웃을 지원하는 경우 특정 채널/계정에서 로그아웃합니다.web.login.start는 현재 QR 가능 웹 채널 공급자의 QR/웹 로그인 흐름을 시작합니다.web.login.wait는 해당 QR/웹 로그인 흐름이 완료될 때까지 기다리고 성공 시 채널을 시작합니다.push.test는 등록된 iOS Node에 테스트 APNs 푸시를 보냅니다.voicewake.get은 저장된 깨우기 단어 트리거를 반환합니다.voicewake.set은 깨우기 단어 트리거를 업데이트하고 변경 사항을 브로드캐스트합니다.
메시징 및 로그
send는 채팅 러너 외부에서 채널/계정/스레드 대상 전송을 위한 직접 아웃바운드 전달 RPC입니다.logs.tail은 커서/제한 및 최대 바이트 제어와 함께 구성된 Gateway 파일 로그 꼬리를 반환합니다.
대화 및 TTS
talk.catalog는 음성, 스트리밍 전사, 실시간 음성을 위한 읽기 전용 대화 공급자 카탈로그를 반환합니다. 정식 공급자 ID, 레지스트리 별칭, 레이블, 구성 상태, 선택적 그룹 수준ready결과, 노출된 모델/음성 ID, 정식 모드, 전송 방식, 브레인 전략, 실시간 오디오/기능 플래그를 포함하며 공급자 비밀을 반환하거나 전역 구성을 변경하지 않습니다. 현재 Gateway는 런타임 공급자 선택을 적용한 뒤ready를 설정합니다. 클라이언트는 이전 Gateway와의 호환성을 위해 이것이 없으면 확인되지 않은 것으로 처리해야 합니다.talk.config는 유효한 대화 구성 페이로드를 반환합니다.includeSecrets에는operator.talk.secrets(또는operator.admin)가 필요합니다.talk.session.create는realtime/gateway-relay,transcription/gateway-relay, 또는stt-tts/managed-room을 위한 Gateway 소유 대화 세션을 만듭니다.stt-tts/managed-room의 경우sessionKey를 전달하는operator.write호출자는 범위 지정된 세션 키 가시성을 위해spawnedBy도 전달해야 합니다. 범위 없는sessionKey생성과brain: "direct-tools"에는operator.admin이 필요합니다.talk.session.join은 관리형 룸 세션 토큰을 검증하고, 필요에 따라session.ready또는session.replaced이벤트를 내보내며, 평문 토큰 또는 저장된 토큰 해시 없이 룸/세션 메타데이터와 최근 대화 이벤트를 반환합니다.talk.session.appendAudio는 Gateway 소유 실시간 릴레이 및 전사 세션에 base64 PCM 입력 오디오를 추가합니다.talk.session.startTurn,talk.session.endTurn,talk.session.cancelTurn은 상태가 지워지기 전에 오래된 턴을 거부하면서 관리형 룸 턴 수명 주기를 구동합니다.talk.session.cancelOutput은 주로 Gateway 릴레이 세션에서 VAD 기반 끼어들기를 위해 어시스턴트 오디오 출력을 중지합니다.talk.session.submitToolResult는 Gateway 소유 실시간 릴레이 세션에서 내보낸 공급자 도구 호출을 완료합니다. 최종 결과가 뒤따를 때의 중간 도구 출력에는options: { willContinue: true }를 전달하고, 다른 실시간 어시스턴트 응답을 시작하지 않고 도구 결과가 공급자 호출을 충족해야 할 때는options: { suppressResponse: true }를 전달합니다.talk.session.steer는 활성 실행 음성 제어를 Gateway 소유 에이전트 기반 대화 세션으로 보냅니다.{ sessionId, text, mode? }를 받으며, 여기서mode는status,steer,cancel, 또는followup입니다. 생략된 모드는 음성 텍스트에서 분류됩니다.talk.session.close는 Gateway 소유 릴레이, 전사, 또는 관리형 룸 세션을 닫고 종료 대화 이벤트를 내보냅니다.talk.mode는 WebChat/Control UI 클라이언트를 위한 현재 대화 모드 상태를 설정/브로드캐스트합니다.talk.client.create는 Gateway가 구성, 자격 증명, 지침, 도구 정책을 소유하는 동안webrtc또는provider-websocket을 사용해 클라이언트 소유 실시간 공급자 세션을 만듭니다.talk.client.toolCall은 클라이언트 소유 실시간 전송이 공급자 도구 호출을 Gateway 정책으로 전달할 수 있게 합니다. 첫 번째 지원 도구는openclaw_agent_consult입니다. 클라이언트는 공급자별 도구 결과를 제출하기 전에 실행 ID를 받고 일반 채팅 수명 주기 이벤트를 기다립니다.talk.client.steer는 클라이언트 소유 실시간 전송을 위한 활성 실행 음성 제어를 보냅니다. Gateway는sessionKey에서 활성 임베디드 실행을 확인하고, 조종을 조용히 폐기하는 대신 구조화된 수락/거부 결과를 반환합니다.talk.event는 실시간, 전사, STT/TTS, 관리형 룸, 전화 통신, 미팅 어댑터를 위한 단일 대화 이벤트 채널입니다.talk.speak는 활성 대화 음성 공급자를 통해 음성을 합성합니다.tts.status는 TTS 활성화 상태, 활성 공급자, 폴백 공급자, 공급자 구성 상태를 반환합니다.tts.providers는 표시 가능한 TTS 공급자 인벤토리를 반환합니다.tts.enable및tts.disable은 TTS 환경설정 상태를 전환합니다.tts.setProvider는 선호 TTS 공급자를 업데이트합니다.tts.convert는 일회성 텍스트 음성 변환을 실행합니다.
비밀, 구성, 업데이트, 마법사
secrets.reload는 활성 SecretRefs를 다시 확인하고 전체 성공 시에만 런타임 비밀 상태를 교체합니다.secrets.resolve는 특정 명령/대상 세트에 대한 명령 대상 비밀 할당을 확인합니다.config.get은 현재 구성 스냅샷과 해시를 반환합니다.config.set은 검증된 구성 페이로드를 씁니다.config.patch는 부분 구성 업데이트를 병합합니다. 파괴적인 배열 교체에는replacePaths에 영향을 받는 경로가 필요합니다. 배열 항목 아래의 중첩 배열은agents.list[].skills같은[]경로를 사용합니다.config.apply는 전체 구성 페이로드를 검증하고 교체합니다.config.schema는 Control UI 및 CLI 도구가 사용하는 실시간 구성 스키마 페이로드를 반환합니다. 스키마,uiHints, 버전, 생성 메타데이터를 포함하며, 런타임이 로드할 수 있는 경우 Plugin + 채널 스키마 메타데이터도 포함합니다. 스키마에는 UI에서 사용하는 동일한 레이블 및 도움말 텍스트에서 파생된 필드title/description메타데이터가 포함되며, 일치하는 필드 문서가 있는 경우 중첩 객체, 와일드카드, 배열 항목,anyOf/oneOf/allOf구성 분기도 포함됩니다.config.schema.lookup은 하나의 구성 경로에 대한 경로 범위 조회 페이로드를 반환합니다. 정규화된 경로, 얕은 스키마 노드, 일치하는 힌트 +hintPath, 선택적reloadKind, UI/CLI 드릴다운을 위한 직계 하위 요약을 포함합니다.reloadKind는restart,hot, 또는none중 하나이며 요청된 경로에 대한 Gateway 구성 리로드 플래너를 반영합니다. 조회 스키마 노드는 사용자 대상 문서와 일반 검증 필드(title,description,type,enum,const,format,pattern, 숫자/문자열/배열/객체 경계,additionalProperties,deprecated,readOnly,writeOnly같은 플래그)를 유지합니다. 하위 요약은key, 정규화된path,type,required,hasChildren, 선택적reloadKind, 그리고 일치하는hint/hintPath를 노출합니다.update.run은 Gateway 업데이트 흐름을 실행하고 업데이트 자체가 성공한 경우에만 재시작을 예약합니다. 세션이 있는 호출자는continuationMessage를 포함해 시작 시 재시작 계속 큐를 통해 후속 에이전트 턴 하나를 재개할 수 있습니다. 제어 플레인에서 수행하는 패키지 관리자 업데이트 및 감독되는 Git 체크아웃 업데이트는 실행 중인 Gateway 내부에서 패키지 트리를 교체하거나 체크아웃/빌드 출력을 변경하는 대신 분리된 관리형 서비스 핸드오프를 사용합니다. 시작된 핸드오프는result.reason: "managed-service-handoff-started"및handoff.status: "started"와 함께ok: true를 반환합니다. 사용할 수 없거나 실패한 핸드오프는managed-service-handoff-unavailable또는managed-service-handoff-failed와 함께ok: false를 반환하며, 수동 셸 업데이트가 필요한 경우handoff.command도 포함합니다. 사용할 수 없는 핸드오프는 systemd용OPENCLAW_SYSTEMD_UNIT처럼 OpenClaw에 안전한 감독자 경계 또는 지속적인 서비스 ID가 없음을 의미합니다. 시작된 핸드오프 중에는 재시작 센티널이 잠시stats.reason: "restart-health-pending"를 보고할 수 있습니다. 계속은 CLI가 재시작된 Gateway를 검증하고 최종ok센티널을 쓸 때까지 지연됩니다.update.status는 사용 가능한 경우 재시작 후 실행 중인 버전을 포함해 최신 업데이트 재시작 센티널을 새로고침하고 반환합니다.wizard.start,wizard.next,wizard.status,wizard.cancel은 WS RPC를 통해 온보딩 마법사를 노출합니다.
에이전트 및 워크스페이스 헬퍼
agents.list는 유효 모델 및 런타임 메타데이터를 포함해 구성된 에이전트 항목을 반환합니다.agents.create,agents.update,agents.delete는 에이전트 레코드와 워크스페이스 연결을 관리합니다.agents.files.list,agents.files.get,agents.files.set는 에이전트에 노출되는 부트스트랩 워크스페이스 파일을 관리합니다.tasks.list,tasks.get,tasks.cancel은 Gateway 작업 원장을 SDK 및 운영자 클라이언트에 노출합니다.artifacts.list,artifacts.get,artifacts.download는 명시적sessionKey,runId또는taskId범위에 대한 트랜스크립트 파생 아티팩트 요약 및 다운로드를 노출합니다. 실행 및 작업 쿼리는 서버 측에서 소유 세션을 확인하고, 출처가 일치하는 트랜스크립트 미디어만 반환합니다. 안전하지 않거나 로컬 URL 소스는 서버 측에서 가져오는 대신 지원되지 않는 다운로드를 반환합니다.environments.list와environments.status는 SDK 클라이언트를 위해 읽기 전용 Gateway 로컬 및 Node 환경 검색을 노출합니다.agent.identity.get은 에이전트 또는 세션의 유효 어시스턴트 ID를 반환합니다.agent.wait는 실행이 끝날 때까지 기다리고 사용 가능한 경우 터미널 스냅샷을 반환합니다.
세션 제어
sessions.list는 에이전트 런타임 백엔드가 구성된 경우 행별agentRuntime메타데이터를 포함해 현재 세션 인덱스를 반환합니다.sessions.subscribe와sessions.unsubscribe는 현재 WS 클라이언트의 세션 변경 이벤트 구독을 전환합니다.sessions.messages.subscribe와sessions.messages.unsubscribe는 한 세션의 트랜스크립트/메시지 이벤트 구독을 전환합니다.sessions.preview는 특정 세션 키에 대한 제한된 트랜스크립트 미리 보기를 반환합니다.sessions.describe는 정확한 세션 키에 대한 하나의 Gateway 세션 행을 반환합니다.sessions.resolve는 세션 대상을 확인하거나 정규화합니다.sessions.create는 새 세션 항목을 생성합니다.sessions.send는 기존 세션으로 메시지를 보냅니다.sessions.steer는 활성 세션을 위한 중단 후 조정 변형입니다.sessions.abort는 세션의 활성 작업을 중단합니다. 호출자는key와 선택적runId를 전달하거나, Gateway가 세션으로 확인할 수 있는 활성 실행에 대해runId만 전달할 수 있습니다.sessions.patch는 세션 메타데이터/오버라이드를 업데이트하고 확인된 정규 모델과 유효agentRuntime을 보고합니다.sessions.reset,sessions.delete,sessions.compact는 세션 유지 관리를 수행합니다.sessions.get은 저장된 전체 세션 행을 반환합니다.- 채팅 실행은 여전히
chat.history,chat.send,chat.abort,chat.inject를 사용합니다.chat.history는 UI 클라이언트를 위해 표시 정규화됩니다. 인라인 지시문 태그는 보이는 텍스트에서 제거되고, 일반 텍스트 도구 호출 XML 페이로드(<tool_call>...</tool_call>,<function_call>...</function_call>,<tool_calls>...</tool_calls>,<function_calls>...</function_calls>및 잘린 도구 호출 블록 포함)와 유출된 ASCII/전각 모델 제어 토큰이 제거되며, 정확히NO_REPLY/no_reply와 같은 순수 무음 토큰 어시스턴트 행은 생략되고, 너무 큰 행은 플레이스홀더로 대체될 수 있습니다. chat.message.get은 단일 가시 트랜스크립트 항목을 위한 추가적인 제한된 전체 메시지 리더입니다. 클라이언트는sessionKey, 세션 선택이 에이전트 범위인 경우 선택적agentId, 그리고 이전에chat.history를 통해 노출된 트랜스크립트messageId를 전달합니다. Gateway는 저장된 항목이 아직 사용 가능하고 너무 크지 않은 경우 경량 히스토리 잘림 한도 없이 동일한 표시 정규화 투영을 반환합니다.chat.send는 자동 컷오프 전에 시작된 모델 호출에는 빠른 모드를 사용하고, 이후 재시도, 폴백, 도구 결과 또는 이어지는 호출은 빠른 모드 없이 시작하도록 단일 턴fastMode: "auto"를 허용합니다. 컷오프 기본값은 60초이며agents.defaults.models["<provider>/<model>"].params.fastAutoOnSeconds로 모델별 구성할 수 있습니다.chat.send호출자는 해당 요청의 컷오프를 재정의하기 위해 단일 턴fastAutoOnSeconds를 전달할 수 있습니다.
기기 페어링 및 기기 토큰
device.pair.list는 대기 중이거나 승인된 페어링 기기를 반환합니다.device.pair.setupCode는 모바일 설정 코드와 기본적으로 PNG QR 데이터 URL을 생성합니다.operator.admin이 필요하며 광고되는 검색에서는 의도적으로 생략됩니다. 결과에는setupCode, 선택적qrDataUrl,gatewayUrl, 비밀이 아닌auth레이블,urlSource가 포함됩니다.device.pair.approve,device.pair.reject,device.pair.remove는 기기 페어링 레코드를 관리합니다.device.token.rotate는 승인된 역할 및 호출자 범위 한도 내에서 페어링된 기기 토큰을 교체합니다.device.token.revoke는 승인된 역할 및 호출자 범위 한도 내에서 페어링된 기기 토큰을 폐기합니다.
설정 코드는 수명이 짧은 부트스트랩 자격 증명을 포함합니다. 클라이언트는 페어링 흐름 이후에 이를 로그로 남기거나 유지해서는 안 됩니다.
Node 페어링, 호출 및 대기 중인 작업
node.pair.request,node.pair.list,node.pair.approve,node.pair.reject,node.pair.remove,node.pair.verify는 Node 페어링과 부트스트랩 검증을 다룹니다.node.list와node.describe는 알려진/연결된 Node 상태를 반환합니다.node.rename은 페어링된 Node 레이블을 업데이트합니다.node.invoke는 연결된 Node로 명령을 전달합니다.node.invoke.result는 호출 요청의 결과를 반환합니다.node.event는 Node에서 발생한 이벤트를 Gateway로 다시 전달합니다.node.pending.pull과node.pending.ack는 연결된 Node 큐 API입니다.node.pending.enqueue와node.pending.drain은 오프라인/연결 해제된 Node의 지속성 있는 대기 작업을 관리합니다.
승인 계열
exec.approval.request,exec.approval.get,exec.approval.list,exec.approval.resolve는 일회성 exec 승인 요청과 대기 중인 승인 조회/재실행을 다룹니다.exec.approval.waitDecision은 대기 중인 exec 승인 하나를 기다리고 최종 결정(또는 시간 초과 시null)을 반환합니다.exec.approvals.get과exec.approvals.set은 Gateway exec 승인 정책 스냅샷을 관리합니다.exec.approvals.node.get과exec.approvals.node.set은 Node 릴레이 명령을 통해 Node 로컬 exec 승인 정책을 관리합니다.plugin.approval.request,plugin.approval.list,plugin.approval.waitDecision,plugin.approval.resolve는 Plugin 정의 승인 흐름을 다룹니다.
자동화, Skills 및 도구
- 자동화:
wake는 즉시 또는 다음 Heartbeat 깨우기 텍스트 주입을 예약합니다.cron.get,cron.list,cron.status,cron.add,cron.update,cron.remove,cron.run,cron.runs는 예약된 작업을 관리합니다. cron.run은 수동 실행을 위한 큐 삽입 방식 RPC로 유지됩니다. 완료 의미 체계가 필요한 클라이언트는 반환된runId를 읽고cron.runs를 폴링해야 합니다.cron.runs는 선택적 비어 있지 않은runId필터를 허용하므로, 클라이언트는 같은 작업의 다른 히스토리 항목과 경합하지 않고 큐에 들어간 수동 실행 하나를 추적할 수 있습니다.- Skills 및 도구:
commands.list,skills.*,tools.catalog,tools.effective,tools.invoke.
공통 이벤트 계열
chat:chat.inject및 기타 트랜스크립트 전용 채팅 이벤트와 같은 UI 채팅 업데이트입니다. 프로토콜 v4에서 델타 페이로드는deltaText를 전달하며,message는 누적 어시스턴트 스냅샷으로 유지됩니다. 접두사가 아닌 대체는replace=true를 설정하고deltaText를 대체 텍스트로 사용합니다.session.message,session.operation,session.tool: 구독된 세션의 트랜스크립트, 진행 중인 세션 작업, 이벤트 스트림 업데이트입니다.sessions.changed: 세션 인덱스 또는 메타데이터가 변경되었습니다.presence: 시스템 프레즌스 스냅샷 업데이트입니다.tick: 주기적 keepalive / 활성 상태 이벤트입니다.health: Gateway 상태 스냅샷 업데이트입니다.heartbeat: Heartbeat 이벤트 스트림 업데이트입니다.cron: Cron 실행/작업 변경 이벤트입니다.shutdown: Gateway 종료 알림입니다.node.pair.requested/node.pair.resolved: Node 페어링 수명 주기입니다.node.invoke.request: Node 호출 요청 브로드캐스트입니다.device.pair.requested/device.pair.resolved: 페어링된 기기 수명 주기입니다.voicewake.changed: 깨우기 단어 트리거 구성이 변경되었습니다.exec.approval.requested/exec.approval.resolved: exec 승인 수명 주기입니다.plugin.approval.requested/plugin.approval.resolved: Plugin 승인 수명 주기입니다.
Node 헬퍼 메서드
- Node는 자동 허용 검사를 위해 현재 Skills 실행 파일 목록을 가져오도록
skills.bins를 호출할 수 있습니다.
작업 원장 RPC
운영자 클라이언트는 작업 원장 RPC를 통해 Gateway 백그라운드 작업 레코드를 검사하고 취소할 수 있습니다. 이러한 메서드는 원시 런타임 상태가 아니라 정리된 작업 요약을 반환합니다.
tasks.list에는operator.read가 필요합니다.- 매개변수: 선택적
status("queued","running","completed","failed","cancelled"또는"timed_out") 또는 이러한 상태의 배열, 선택적agentId, 선택적sessionKey,1부터500까지의 선택적limit, 선택적 문자열cursor. - 결과:
{ "tasks": TaskSummary[], "nextCursor"?: string }.
- 매개변수: 선택적
tasks.get에는operator.read가 필요합니다.- 매개변수:
{ "taskId": string }. - 결과:
{ "task": TaskSummary }. - 누락된 작업 ID는 Gateway not-found 오류 형태를 반환합니다.
- 매개변수:
tasks.cancel에는operator.write가 필요합니다.- 매개변수:
{ "taskId": string, "reason"?: string }. - 결과:
{ "found": boolean, "cancelled": boolean, "reason"?: string, "task"?: TaskSummary }. found는 원장에 일치하는 작업이 있었는지 보고합니다.cancelled는 런타임이 취소를 수락했거나 기록했는지 보고합니다.
- 매개변수:
TaskSummary에는 id, status와 kind, runtime, title, agentId, sessionKey, childSessionKey, ownerKey, runId, taskId, flowId, parentTaskId, sourceId, 타임스탬프, 진행률, 터미널 요약, 정리된 오류 텍스트와 같은 선택적 메타데이터가 포함됩니다. agentId는 작업을 실행하는 에이전트를 식별합니다. sessionKey와 ownerKey는 요청자 및 제어 컨텍스트를 보존합니다.
운영자 헬퍼 메서드
- 운영자는
commands.list(operator.read)를 호출해 에이전트의 런타임 명령 인벤토리를 가져올 수 있습니다.agentId는 선택 사항입니다. 기본 에이전트 작업 영역을 읽으려면 생략하세요.scope는 기본name이 대상으로 삼는 표면을 제어합니다.text는 앞의/없이 기본 텍스트 명령 토큰을 반환합니다.native및 기본both경로는 사용 가능한 경우 제공자 인식 네이티브 이름을 반환합니다.
textAliases는/model및/m같은 정확한 슬래시 별칭을 전달합니다.nativeName은 존재하는 경우 제공자 인식 네이티브 명령 이름을 전달합니다.provider는 선택 사항이며 네이티브 이름 지정과 네이티브 Plugin 명령 가용성에만 영향을 줍니다.includeArgs=false는 응답에서 직렬화된 인수 메타데이터를 생략합니다.
- 운영자는
tools.catalog(operator.read)를 호출해 에이전트의 런타임 도구 카탈로그를 가져올 수 있습니다. 응답에는 그룹화된 도구와 출처 메타데이터가 포함됩니다.source:core또는pluginpluginId:source="plugin"일 때 Plugin 소유자optional: Plugin 도구가 선택 사항인지 여부
- 운영자는
tools.effective(operator.read)를 호출해 세션의 런타임 유효 도구 인벤토리를 가져올 수 있습니다.sessionKey는 필수입니다.- Gateway는 호출자가 제공한 인증 또는 전달 컨텍스트를 받는 대신 세션에서 신뢰할 수 있는 런타임 컨텍스트를 서버 측에서 도출합니다.
- 응답은 활성 인벤토리의 세션 범위 서버 도출 투영이며, 코어, Plugin, 채널, 이미 발견된 MCP 서버 도구를 포함합니다.
tools.effective는 MCP에 대해 읽기 전용입니다. 준비된 세션 MCP 카탈로그를 최종 도구 정책을 통해 투영할 수 있지만, MCP 런타임을 생성하거나, 전송에 연결하거나,tools/list를 발행하지는 않습니다. 일치하는 준비된 카탈로그가 없으면 응답에mcp-not-yet-connected,mcp-not-yet-listed,mcp-stale-catalog같은 알림이 포함될 수 있습니다.- 유효 도구 항목은
source="core",source="plugin",source="channel"또는source="mcp"를 사용합니다.
- 운영자는
tools.invoke(operator.write)를 호출해/tools/invoke와 동일한 Gateway 정책 경로를 통해 사용 가능한 도구 하나를 호출할 수 있습니다.name은 필수입니다.args,sessionKey,agentId,confirm,idempotencyKey는 선택 사항입니다.sessionKey와agentId가 모두 있으면 확인된 세션 에이전트가agentId와 일치해야 합니다.cron,gateway,nodes같은 소유자 전용 코어 래퍼는tools.invoke메서드 자체가operator.write이더라도 소유자/관리자 신원(operator.admin)을 요구합니다.- 응답은
ok,toolName, 선택적output, 타입이 지정된error필드가 있는 SDK 대상 봉투입니다. 승인 또는 정책 거부는 Gateway 도구 정책 파이프라인을 우회하지 않고 페이로드에서ok:false를 반환합니다.
- 운영자는
skills.status(operator.read)를 호출해 에이전트의 보이는 skill 인벤토리를 가져올 수 있습니다.agentId는 선택 사항입니다. 기본 에이전트 작업 영역을 읽으려면 생략하세요.- 응답에는 원시 비밀 값을 노출하지 않고 적격성, 누락된 요구 사항, 구성 검사, 정리된 설치 옵션이 포함됩니다.
- 운영자는 ClawHub 발견 메타데이터를 위해
skills.search와skills.detail(operator.read)을 호출할 수 있습니다. - 운영자는
skills.upload.begin,skills.upload.chunk,skills.upload.commit(operator.admin)을 호출해 설치 전에 비공개 skill 아카이브를 스테이징할 수 있습니다. 이는 신뢰할 수 있는 클라이언트를 위한 별도의 관리자 업로드 경로이며, 일반 ClawHub skill 설치 흐름이 아닙니다.skills.install.allowUploadedArchives가 활성화되어 있지 않으면 기본적으로 비활성화됩니다.skills.upload.begin({ kind: "skill-archive", slug, sizeBytes, sha256?, force?, idempotencyKey? })는 해당 slug 및 force 값에 바인딩된 업로드를 생성합니다.skills.upload.chunk({ uploadId, offset, dataBase64 })는 정확히 디코딩된 오프셋에 바이트를 추가합니다.skills.upload.commit({ uploadId, sha256? })는 최종 크기와 SHA-256을 검증합니다. 커밋은 업로드만 완료하며 skill을 설치하지 않습니다.- 업로드된 skill 아카이브는
SKILL.md루트를 포함하는 zip 아카이브입니다. 아카이브의 내부 디렉터리 이름은 설치 대상을 선택하지 않습니다.
- 운영자는 세 가지 모드로
skills.install(operator.admin)을 호출할 수 있습니다.- ClawHub 모드:
{ source: "clawhub", slug, version?, force? }는 skill 폴더를 기본 에이전트 작업 영역skills/디렉터리에 설치합니다. - 업로드 모드:
{ source: "upload", uploadId, slug, force?, sha256?, timeoutMs? }는 커밋된 업로드를 기본 에이전트 작업 영역skills/<slug>디렉터리에 설치합니다. slug와 force 값은 원래skills.upload.begin요청과 일치해야 합니다. 이 모드는skills.install.allowUploadedArchives가 활성화되어 있지 않으면 거부됩니다. 이 설정은 ClawHub 설치에 영향을 주지 않습니다. - Gateway 설치 관리자 모드:
{ name, installId, timeoutMs? }는 Gateway 호스트에서 선언된metadata.openclaw.install작업을 실행합니다. 이전 클라이언트는 여전히dangerouslyForceUnsafeInstall을 보낼 수 있습니다. 이 필드는 더 이상 사용되지 않으며, 프로토콜 호환성을 위해서만 허용되고 무시됩니다. 운영자가 소유한 설치 결정에는security.installPolicy를 사용하세요.
- ClawHub 모드:
- 운영자는 두 가지 모드로
skills.update(operator.admin)를 호출할 수 있습니다.- ClawHub 모드는 기본 에이전트 작업 영역에서 추적 중인 slug 하나 또는 추적 중인 모든 ClawHub 설치를 업데이트합니다.
- 구성 모드는
enabled,apiKey,env같은skills.entries.<skillKey>값을 패치합니다.
models.list 보기
models.list는 선택적 view 매개변수를 받습니다.
- 생략 또는
"default": 현재 런타임 동작입니다.agents.defaults.models가 구성되어 있으면, 응답은provider/*항목에 대해 동적으로 발견된 모델을 포함한 허용 카탈로그입니다. 그렇지 않으면 응답은 전체 Gateway 카탈로그입니다. "configured": 선택기 크기의 동작입니다.agents.defaults.models가 구성되어 있으면,provider/*항목에 대한 제공자 범위 발견을 포함해 여전히 우선 적용됩니다. 허용 목록이 없으면 응답은 명시적models.providers.*.models항목을 사용하며, 구성된 모델 행이 없을 때만 전체 카탈로그로 대체됩니다."all":agents.defaults.models를 우회하는 전체 Gateway 카탈로그입니다. 일반 모델 선택기가 아니라 진단 및 발견 UI에 사용하세요.
실행 승인
- 실행 요청에 승인이 필요하면 Gateway는
exec.approval.requested를 브로드캐스트합니다. - 운영자 클라이언트는
exec.approval.resolve를 호출해 해결합니다(operator.approvals범위 필요). host=node의 경우exec.approval.request에는systemRunPlan(표준argv/cwd/rawCommand/세션 메타데이터)이 포함되어야 합니다.systemRunPlan이 없는 요청은 거부됩니다.- 승인 후 전달된
node.invoke system.run호출은 해당 표준systemRunPlan을 권한 있는 명령/cwd/세션 컨텍스트로 재사용합니다. - 호출자가 준비와 최종 승인된
system.run전달 사이에command,rawCommand,cwd,agentId또는sessionKey를 변경하면 Gateway는 변경된 페이로드를 신뢰하는 대신 실행을 거부합니다.
에이전트 전달 대체 경로
agent요청은 아웃바운드 전달을 요청하기 위해deliver=true를 포함할 수 있습니다.bestEffortDeliver=false는 엄격한 동작을 유지합니다. 확인되지 않았거나 내부 전용인 전달 대상은INVALID_REQUEST를 반환합니다.bestEffortDeliver=true는 외부로 전달 가능한 경로를 확인할 수 없을 때 세션 전용 실행으로 대체되는 것을 허용합니다. 예를 들어 내부/webchat 세션 또는 모호한 다중 채널 구성입니다.- 최종
agent결과는 전달이 요청된 경우result.deliveryStatus를 포함할 수 있으며,openclaw agent --json --deliver에 문서화된 것과 동일한sent,suppressed,partial_failed,failed상태를 사용합니다.
버전 관리
PROTOCOL_VERSION은packages/gateway-protocol/src/version.ts에 있습니다.- 클라이언트는
minProtocol+maxProtocol을 보냅니다. 서버는 현재 프로토콜을 포함하지 않는 범위를 거부합니다. 현재 클라이언트와 서버는 프로토콜 v4를 요구합니다. - 스키마와 모델은 TypeBox 정의에서 생성됩니다.
pnpm protocol:genpnpm protocol:gen:swiftpnpm protocol:check
클라이언트 상수
src/gateway/client.ts의 참조 클라이언트는 다음 기본값을 사용합니다. 값은
프로토콜 v4 전반에서 안정적이며 타사 클라이언트의 예상 기준선입니다.
| 상수 | 기본값 | 출처 |
|---|---|---|
PROTOCOL_VERSION |
4 |
packages/gateway-protocol/src/version.ts |
MIN_CLIENT_PROTOCOL_VERSION |
4 |
packages/gateway-protocol/src/version.ts |
| 요청 제한 시간(RPC당) | 30_000 ms |
src/gateway/client.ts(requestTimeoutMs) |
| 사전 인증 / 연결 챌린지 제한 시간 | 15_000 ms |
src/gateway/handshake-timeouts.ts(config/env가 짝을 이루는 서버/클라이언트 예산을 늘릴 수 있음) |
| 초기 재연결 백오프 | 1_000 ms |
src/gateway/client.ts(backoffMs) |
| 최대 재연결 백오프 | 30_000 ms |
src/gateway/client.ts(scheduleReconnect) |
| device-token 종료 후 빠른 재시도 제한 | 250 ms |
src/gateway/client.ts |
terminate() 전 강제 중지 유예 |
250 ms |
FORCE_STOP_TERMINATE_GRACE_MS |
stopAndWait() 기본 제한 시간 |
1_000 ms |
STOP_AND_WAIT_TIMEOUT_MS |
기본 tick 간격(hello-ok 전) |
30_000 ms |
src/gateway/client.ts |
| tick 제한 시간 종료 | 침묵이 tickIntervalMs * 2를 초과하면 code 4000 |
src/gateway/client.ts |
MAX_PAYLOAD_BYTES |
25 * 1024 * 1024(25 MB) |
src/gateway/server-constants.ts |
서버는 hello-ok에서 유효한 policy.tickIntervalMs, policy.maxPayload,
policy.maxBufferedBytes를 알립니다. 클라이언트는 핸드셰이크 전 기본값이 아니라
이 값을 준수해야 합니다.
인증
- 공유 시크릿 Gateway 인증은 구성된 인증 모드에 따라
connect.params.auth.token또는connect.params.auth.password를 사용합니다. - Tailscale Serve(
gateway.auth.allowTailscale: true) 또는 non-loopbackgateway.auth.mode: "trusted-proxy"같은 ID 포함 모드는connect.params.auth.*대신 요청 헤더에서 연결 인증 검사를 충족합니다. - 프라이빗 인그레스
gateway.auth.mode: "none"은 공유 시크릿 연결 인증을 완전히 건너뜁니다. 이 모드를 공개/신뢰할 수 없는 인그레스에 노출하지 마세요. - 페어링 후 Gateway는 연결 역할 + 범위로 제한된 디바이스 토큰을 발급합니다.
이 토큰은
hello-ok.auth.deviceToken에 반환되며, 클라이언트가 이후 연결에 사용하도록 영구 저장해야 합니다. - 클라이언트는 성공한 모든 연결 후 기본
hello-ok.auth.deviceToken을 영구 저장해야 합니다. - 해당 저장된 디바이스 토큰으로 다시 연결할 때는 그 토큰에 대해 저장된 승인된 범위 집합도 재사용해야 합니다. 이렇게 하면 이미 부여된 읽기/프로브/상태 접근이 보존되고, 재연결이 더 좁은 암시적 관리자 전용 범위로 조용히 축소되는 일을 피할 수 있습니다.
- 클라이언트 측 연결 인증 조립(
src/gateway/client.ts의selectConnectAuth):auth.password는 독립적이며 설정된 경우 항상 전달됩니다.auth.token은 우선순위에 따라 채워집니다. 명시적 공유 토큰이 먼저이고, 그다음 명시적deviceToken, 그다음 저장된 디바이스별 토큰(deviceId+role기준)입니다.auth.bootstrapToken은 위 항목 중 어떤 것도auth.token으로 해석되지 않았을 때만 전송됩니다. 공유 토큰 또는 해석된 디바이스 토큰이 있으면 전송이 억제됩니다.- 일회성
AUTH_TOKEN_MISMATCH재시도에서 저장된 디바이스 토큰의 자동 승격은 신뢰할 수 있는 엔드포인트에만 제한됩니다. 즉, loopback 또는 고정된tlsFingerprint가 있는wss://입니다. 고정이 없는 공개wss://는 해당하지 않습니다.
- 기본 제공 설정 코드 부트스트랩은 기본 노드
hello-ok.auth.deviceToken과 신뢰할 수 있는 모바일 인계를 위한 제한된 운영자 토큰을hello-ok.auth.deviceTokens에 반환합니다. 운영자 토큰에는 네이티브 Talk 구성 읽기를 위한operator.talk.secrets가 포함되지만, 페어링 변경 범위와operator.admin은 제외됩니다. - 비기준 설정 코드 부트스트랩이 승인을 기다리는 동안
PAIRING_REQUIRED세부 정보에는recommendedNextStep: "wait_then_retry",retryable: true,pauseReconnect: false가 포함됩니다. 클라이언트는 요청이 승인되거나 토큰이 무효화될 때까지 같은 부트스트랩 토큰으로 계속 재연결해야 합니다. hello-ok.auth.deviceTokens는 연결이wss://또는 loopback/local 페어링 같은 신뢰할 수 있는 전송에서 부트스트랩 인증을 사용한 경우에만 영구 저장하세요.- 클라이언트가 명시적
deviceToken또는 명시적scopes를 제공하면 해당 호출자가 요청한 범위 집합이 권위 있는 값으로 유지됩니다. 캐시된 범위는 클라이언트가 저장된 디바이스별 토큰을 재사용할 때만 재사용됩니다. - 디바이스 토큰은
device.token.rotate및device.token.revoke를 통해 교체/해지할 수 있습니다(operator.pairing범위 필요). 노드 또는 다른 비운영자 역할을 교체하거나 해지하려면operator.admin도 필요합니다. device.token.rotate는 교체 메타데이터를 반환합니다. 이미 해당 디바이스 토큰으로 인증된 동일 디바이스 호출에만 대체 bearer 토큰을 그대로 반환하므로, 토큰 전용 클라이언트는 재연결 전에 대체 토큰을 영구 저장할 수 있습니다. 공유/관리자 교체는 bearer 토큰을 반환하지 않습니다.- 토큰 발급, 교체, 해지는 해당 디바이스의 페어링 항목에 기록된 승인된 역할 집합으로 제한됩니다. 토큰 변경은 페어링 승인이 부여하지 않은 디바이스 역할을 확장하거나 대상으로 지정할 수 없습니다.
- 페어링된 디바이스 토큰 세션에서 디바이스 관리는 호출자에게
operator.admin도 있는 경우를 제외하고 자체 범위로 제한됩니다. 관리자가 아닌 호출자는 자신의 디바이스 항목에 대한 운영자 토큰만 관리할 수 있습니다. 노드 및 다른 비운영자 토큰 관리는 호출자 자신의 디바이스라도 관리자 전용입니다. device.token.rotate와device.token.revoke는 대상 운영자 토큰 범위 집합도 호출자의 현재 세션 범위와 비교해 검사합니다. 관리자가 아닌 호출자는 자신이 이미 보유한 것보다 더 넓은 운영자 토큰을 교체하거나 해지할 수 없습니다.- 인증 실패에는
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회 재시도를 시도할 수 있습니다.
- 해당 재시도도 실패하면 클라이언트는 자동 재연결 루프를 중지하고 운영자 조치 안내를 표시해야 합니다.
AUTH_SCOPE_MISMATCH는 디바이스 토큰이 인식되었지만 요청된 역할/범위를 포함하지 않는다는 뜻입니다. 클라이언트는 이를 잘못된 토큰으로 표시하지 말고, 운영자에게 다시 페어링하거나 더 좁거나 넓은 범위 계약을 승인하라고 안내해야 합니다.
디바이스 ID + 페어링
- 노드는 키 쌍 지문에서 파생된 안정적인 디바이스 ID(
device.id)를 포함해야 합니다. - Gateway는 디바이스 + 역할별로 토큰을 발급합니다.
- local 자동 승인이 활성화되지 않은 한 새 디바이스 ID에는 페어링 승인이 필요합니다.
- 페어링 자동 승인은 직접 local loopback 연결을 중심으로 합니다.
- OpenClaw에는 신뢰할 수 있는 공유 시크릿 헬퍼 흐름을 위한 좁은 백엔드/컨테이너-local 자체 연결 경로도 있습니다.
- 같은 호스트의 tailnet 또는 LAN 연결도 페어링에서는 여전히 원격으로 처리되며 승인이 필요합니다.
- WS 클라이언트는 일반적으로
connect중에deviceID를 포함합니다(운영자 + 노드). 디바이스가 없는 운영자 예외는 명시적 신뢰 경로뿐입니다.- localhost 전용 비보안 HTTP 호환성을 위한
gateway.controlUi.allowInsecureAuth=true. - 성공한
gateway.auth.mode: "trusted-proxy"운영자 Control UI 인증. gateway.controlUi.dangerouslyDisableDeviceAuth=true(비상용, 심각한 보안 저하).- 예약된 내부 헬퍼 경로에서의 direct-loopback
gateway-client백엔드 RPC.
- localhost 전용 비보안 HTTP 호환성을 위한
- 디바이스 ID를 생략하면 범위에 영향이 있습니다. 명시적 신뢰 경로를 통해 디바이스 없는
운영자 연결이 허용되더라도 OpenClaw는 해당 경로에 이름 있는 범위 보존 예외가 없는 한
자체 선언 범위를 빈 집합으로 지웁니다. 그러면 범위 제한 메서드는
missing scope로 실패합니다. gateway.controlUi.dangerouslyDisableDeviceAuth=true는 Control UI 비상용 범위 보존 경로입니다. 임의의 사용자 지정 백엔드 또는 CLI 형태의 WebSocket 클라이언트에 범위를 부여하지 않습니다.- 예약된 direct-loopback
gateway-client백엔드 헬퍼 경로는 내부 local 제어 평면 RPC에 대해서만 범위를 보존합니다. 사용자 지정 백엔드 ID는 이 예외를 받지 않습니다. - 모든 연결은 서버가 제공한
connect.challengenonce에 서명해야 합니다.
디바이스 인증 마이그레이션 진단
아직 challenge 이전 서명 동작을 사용하는 레거시 클라이언트의 경우, 이제 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 + 고정
- TLS는 WS 연결에 지원됩니다.
- 클라이언트는 선택적으로 Gateway 인증서 지문을 고정할 수 있습니다(
gateway.tls구성과gateway.remote.tlsFingerprint또는 CLI--tls-fingerprint참조).
범위
이 프로토콜은 전체 Gateway API(상태, 채널, 모델, 채팅,
에이전트, 세션, 노드, 승인 등)를 노출합니다. 정확한 표면은
packages/gateway-protocol/src/schema.ts의 TypeBox 스키마로 정의됩니다.