Gateway 아키텍처

마지막 업데이트: 2026-01-22

개요

  • 단일 장기 실행 Gateway가 모든 메시징 표면을 소유합니다 (Baileys를 통한 WhatsApp, grammY를 통한 Telegram, Slack, Discord, Signal, iMessage, WebChat).
  • Control-plane 클라이언트(macOS app, CLI, web UI, 자동화)는 구성된 bind host(기본값 127.0.0.1:18789)에서 WebSocket을 통해 Gateway에 연결합니다.
  • Node(macOS/iOS/Android/헤드리스)도 WebSocket을 통해 연결하지만, 명시적인 cap/command와 함께 role: node를 선언합니다.
  • host당 하나의 Gateway; WhatsApp session을 여는 유일한 곳입니다.
  • canvas host(기본값 18793)는 agent 편집 가능한 HTML 및 A2UI를 제공합니다.

구성 요소 및 플로우

Gateway (데몬)

  • Provider 연결을 유지합니다.
  • 타입이 지정된 WS API를 노출합니다 (요청, 응답, 서버 푸시 event).
  • JSON Schema에 대해 인바운드 프레임을 검증합니다.
  • agent, chat, presence, health, heartbeat, cron과 같은 event를 발행합니다.

클라이언트 (mac app / CLI / web admin)

  • 클라이언트당 하나의 WS 연결.
  • 요청 전송 (health, status, send, agent, system-presence).
  • event 구독 (tick, agent, presence, shutdown).

Node (macOS / iOS / Android / 헤드리스)

  • role: node와 함께 동일한 WS 서버에 연결합니다.
  • connect에서 장치 identity를 제공합니다; 페어링은 장치 기반(role node)이며 승인은 장치 페어링 저장소에 있습니다.
  • canvas.*, camera.*, screen.record, location.get과 같은 명령을 노출합니다.

프로토콜 세부 정보:

WebChat

  • Gateway WS API를 사용하여 채팅 기록 및 전송을 위한 정적 UI.
  • 원격 설정에서는 다른 클라이언트와 동일한 SSH/Tailscale 터널을 통해 연결합니다.

연결 생명주기 (단일 클라이언트)

Client                    Gateway
  |                          |
  |---- req:connect -------->|
  |<------ res (ok) ---------|   (또는 res error + close)
  |   (payload=hello-ok는 snapshot 전달: presence + health)
  |                          |
  |<------ event:presence ---|
  |<------ event:tick -------|
  |                          |
  |------- req:agent ------->|
  |<------ res:agent --------|   (ack: {runId,status:"accepted"})
  |<------ event:agent ------|   (스트리밍)
  |<------ res:agent --------|   (final: {runId,status,summary})
  |                          |

Wire 프로토콜 (요약)

  • 전송: WebSocket, JSON payload가 있는 텍스트 프레임.
  • 첫 번째 프레임은 반드시 connect여야 합니다.
  • 핸드셰이크 후:
    • 요청: {type:"req", id, method, params}{type:"res", id, ok, payload|error}
    • Event: {type:"event", event, payload, seq?, stateVersion?}
  • OPENCLAW_GATEWAY_TOKEN(또는 --token)이 설정된 경우, connect.params.auth.token이 일치해야 하며 그렇지 않으면 소켓이 닫힙니다.
  • Idempotency key는 부작용이 있는 메서드(send, agent)에 필요하며 안전하게 재시도할 수 있습니다; 서버는 단기 중복 제거 캐시를 유지합니다.
  • Node는 connect에서 role: "node" 및 cap/command/permission을 포함해야 합니다.

페어링 + 로컬 신뢰

  • 모든 WS 클라이언트(운영자 + node)는 connect에서 장치 identity를 포함합니다.
  • 새 장치 ID는 페어링 승인이 필요합니다; Gateway는 후속 연결을 위해 장치 token을 발급합니다.
  • 로컬 연결(루프백 또는 gateway host 자체의 tailnet 주소)은 동일 host UX를 원활하게 유지하기 위해 자동 승인될 수 있습니다.
  • 비로컬 연결은 connect.challenge nonce에 서명해야 하며 명시적 승인이 필요합니다.
  • Gateway 인증(gateway.auth.*)은 여전히 모든 연결(로컬 또는 원격)에 적용됩니다.

세부 정보: Gateway protocol, Pairing, Security.

프로토콜 타이핑 및 codegen

  • TypeBox schema가 프로토콜을 정의합니다.
  • JSON Schema는 해당 schema에서 생성됩니다.
  • Swift model은 JSON Schema에서 생성됩니다.

원격 액세스

  • 선호: Tailscale 또는 VPN.
  • 대안: SSH 터널
    ssh -N -L 18789:127.0.0.1:18789 user@host
    
  • 동일한 핸드셰이크 + auth token이 터널을 통해 적용됩니다.
  • 원격 설정에서 WS에 대해 TLS + 선택적 pinning을 활성화할 수 있습니다.

운영 스냅샷

  • 시작: openclaw gateway (포그라운드, stdout에 로그).
  • Health: WS를 통한 health (또한 hello-ok에 포함됨).
  • 감독: 자동 재시작을 위한 launchd/systemd.

불변성

  • 정확히 하나의 Gateway가 host당 단일 Baileys session을 제어합니다.
  • 핸드셰이크는 필수입니다; 비 JSON 또는 비 connect 첫 번째 프레임은 하드 닫기입니다.
  • Event는 재생되지 않습니다; 클라이언트는 gap에서 새로 고침해야 합니다.