iOS 앱 (노드)
사용 가능 여부: 내부 프리뷰. iOS 앱은 아직 공개 배포되지 않았습니다.
기능 설명
- WebSocket(LAN 또는 테일넷)을 통해 게이트웨이에 연결합니다.
- 노드 기능을 제공합니다: Canvas, 화면 스냅샷, 카메라 캡처, 위치, 대화 모드, Voice Wake.
node.invoke명령을 수신하고 노드 상태 이벤트를 보고합니다.
요구 사항
- 다른 디바이스(macOS, Linux 또는 Windows WSL2)에서 게이트웨이가 실행 중이어야 합니다.
- 네트워크 경로:
- Bonjour를 통한 동일 LAN, 또는
- 유니캐스트 DNS-SD를 통한 테일넷 (예시 도메인:
openclaw.internal.), 또는 - 수동 호스트/포트 (대안).
빠른 시작 (페어링 + 연결)
- 게이트웨이를 시작합니다:
openclaw gateway --port 18789
-
iOS 앱에서 설정을 열고 발견된 게이트웨이를 선택합니다 (또는 수동 호스트를 활성화하고 호스트/포트를 입력합니다).
-
게이트웨이 호스트에서 페어링 요청을 승인합니다:
openclaw devices list
openclaw devices approve <requestId>
- 연결을 확인합니다:
openclaw nodes status
openclaw gateway call node.list --params "{}"
공식 빌드를 위한 릴레이 기반 푸시
공식 배포된 iOS 빌드는 원시 APNs 토큰을 게이트웨이에 공개하는 대신 외부 푸시 릴레이를 사용합니다.
게이트웨이 측 요구 사항:
{
gateway: {
push: {
apns: {
relay: {
baseUrl: "https://relay.example.com",
},
},
},
},
}
흐름 동작 방식:
- iOS 앱은 App Attest와 앱 영수증을 사용하여 릴레이에 등록합니다.
- 릴레이는 불투명한 릴레이 핸들과 등록 범위의 전송 권한을 반환합니다.
- iOS 앱은 페어링된 게이트웨이 아이덴티티를 가져와 릴레이 등록에 포함하여, 릴레이 기반 등록이 해당 게이트웨이에 위임됩니다.
- 앱은 해당 릴레이 기반 등록을
push.apns.register로 페어링된 게이트웨이에 전달합니다. - 게이트웨이는 저장된 릴레이 핸들을
push.test, 백그라운드 웨이크, 웨이크 넛지에 사용합니다. - 게이트웨이 릴레이 기본 URL은 공식/TestFlight iOS 빌드에 내장된 릴레이 URL과 일치해야 합니다.
- 앱이 이후 다른 게이트웨이나 다른 릴레이 기본 URL을 가진 빌드에 연결하면, 기존 바인딩을 재사용하지 않고 릴레이 등록을 갱신합니다.
이 경로에서 게이트웨이에 불필요한 것:
- 배포 전체에 걸친 릴레이 토큰이 필요 없음.
- 공식/TestFlight 릴레이 기반 전송을 위한 직접 APNs 키가 필요 없음.
운영자를 위한 예상 흐름:
- 공식/TestFlight iOS 빌드를 설치합니다.
- 게이트웨이에
gateway.push.apns.relay.baseUrl을 설정합니다. - 앱을 게이트웨이에 페어링하고 연결이 완료될 때까지 기다립니다.
- 앱은 APNs 토큰을 받고, 운영자 세션이 연결되고, 릴레이 등록이 성공한 후 자동으로
push.apns.register를 게시합니다. - 이후
push.test, 재연결 웨이크, 웨이크 넛지가 저장된 릴레이 기반 등록을 사용할 수 있습니다.
호환성 참고:
OPENCLAW_APNS_RELAY_BASE_URL은 게이트웨이의 임시 환경 변수 오버라이드로 계속 사용 가능합니다.
인증 및 신뢰 흐름
릴레이는 공식 iOS 빌드에서 직접 APNs-on-gateway가 제공할 수 없는 두 가지 제약을 적용하기 위해 존재합니다:
- Apple을 통해 배포된 정품 OpenClaw iOS 빌드만 호스팅된 릴레이를 사용할 수 있습니다.
- 게이트웨이는 해당 게이트웨이에 페어링된 iOS 디바이스에 대해서만 릴레이 기반 푸시를 보낼 수 있습니다.
단계별 흐름:
-
iOS 앱 -> 게이트웨이- 앱은 먼저 일반적인 게이트웨이 인증 흐름을 통해 게이트웨이와 페어링합니다.
- 이를 통해 인증된 노드 세션과 인증된 운영자 세션을 얻습니다.
- 운영자 세션은
gateway.identity.get을 호출하는 데 사용됩니다.
-
iOS 앱 -> 릴레이- 앱은 HTTPS를 통해 릴레이 등록 엔드포인트를 호출합니다.
- 등록에는 App Attest 증명과 앱 영수증이 포함됩니다.
- 릴레이는 번들 ID, App Attest 증명, Apple 영수증을 검증하고, 공식/프로덕션 배포 경로를 요구합니다.
- 이것이 로컬 Xcode/개발 빌드가 호스팅된 릴레이를 사용하지 못하게 차단하는 이유입니다. 로컬 빌드는 서명될 수 있지만, 릴레이가 기대하는 공식 Apple 배포 증명을 만족시키지 못합니다.
-
게이트웨이 아이덴티티 위임- 릴레이 등록 전에, 앱은
gateway.identity.get에서 페어링된 게이트웨이 아이덴티티를 가져옵니다. - 앱은 릴레이 등록 페이로드에 해당 게이트웨이 아이덴티티를 포함합니다.
- 릴레이는 해당 게이트웨이 아이덴티티에 위임된 릴레이 핸들과 등록 범위의 전송 권한을 반환합니다.
- 릴레이 등록 전에, 앱은
-
게이트웨이 -> 릴레이- 게이트웨이는
push.apns.register에서 릴레이 핸들과 전송 권한을 저장합니다. push.test, 재연결 웨이크, 웨이크 넛지 시 게이트웨이는 자체 디바이스 아이덴티티로 전송 요청에 서명합니다.- 릴레이는 저장된 전송 권한과 등록 시 위임된 게이트웨이 아이덴티티에 대해 게이트웨이 서명을 검증합니다.
- 다른 게이트웨이는 핸들을 어떻게든 획득하더라도 해당 저장된 등록을 재사용할 수 없습니다.
- 게이트웨이는
-
릴레이 -> APNs- 릴레이는 프로덕션 APNs 자격 증명과 공식 빌드의 원시 APNs 토큰을 소유합니다.
- 게이트웨이는 릴레이 기반 공식 빌드에 대해 원시 APNs 토큰을 저장하지 않습니다.
- 릴레이는 페어링된 게이트웨이를 대신하여 APNs에 최종 푸시를 보냅니다.
이 설계의 이유:
- 프로덕션 APNs 자격 증명을 사용자 게이트웨이에 노출시키지 않기 위해.
- 공식 빌드 APNs 토큰을 게이트웨이에 저장하지 않기 위해.
- 호스팅된 릴레이 사용을 공식/TestFlight OpenClaw 빌드에만 허용하기 위해.
- 한 게이트웨이가 다른 게이트웨이 소유의 iOS 디바이스에 웨이크 푸시를 보내는 것을 방지하기 위해.
로컬/수동 빌드는 직접 APNs를 사용합니다. 릴레이 없이 이러한 빌드를 테스트하는 경우, 게이트웨이에 직접 APNs 자격 증명이 필요합니다:
export OPENCLAW_APNS_TEAM_ID="TEAMID"
export OPENCLAW_APNS_KEY_ID="KEYID"
export OPENCLAW_APNS_PRIVATE_KEY_P8="$(cat /path/to/AuthKey_KEYID.p8)"
디스커버리 경로
Bonjour (LAN)
게이트웨이는 local.에서 _openclaw-gw._tcp를 광고합니다. iOS 앱은 이를 자동으로 나열합니다.
테일넷 (크로스 네트워크)
mDNS가 차단된 경우, 유니캐스트 DNS-SD 존(도메인 선택, 예: openclaw.internal.)과 Tailscale 분할 DNS를 사용하세요.
CoreDNS 예시는 Bonjour를 참조하세요.
수동 호스트/포트
설정에서 Manual Host를 활성화하고 게이트웨이 호스트 + 포트(기본 18789)를 입력하세요.
Canvas + A2UI
iOS 노드는 WKWebView Canvas를 렌더링합니다. node.invoke로 제어하세요:
openclaw nodes invoke --node "iOS Node" --command canvas.navigate --params '{"url":"http://<gateway-host>:18789/__openclaw__/canvas/"}'
참고:
- 게이트웨이 Canvas 호스트는
/__openclaw__/canvas/와/__openclaw__/a2ui/를 서빙합니다. - 게이트웨이 HTTP 서버(포트
gateway.port와 동일, 기본18789)에서 서빙됩니다. - iOS 노드는 연결 시 Canvas 호스트 URL이 광고되면 A2UI로 자동 이동합니다.
canvas.navigate와{"url":""}로 내장 스캐폴드로 돌아갑니다.
Canvas eval / snapshot
openclaw nodes invoke --node "iOS Node" --command canvas.eval --params '{"javaScript":"(() => { const {ctx} = window.__openclaw; ctx.clearRect(0,0,innerWidth,innerHeight); ctx.lineWidth=6; ctx.strokeStyle=\"#ff2d55\"; ctx.beginPath(); ctx.moveTo(40,40); ctx.lineTo(innerWidth-40, innerHeight-40); ctx.stroke(); return \"ok\"; })()"}'
openclaw nodes invoke --node "iOS Node" --command canvas.snapshot --params '{"maxWidth":900,"format":"jpeg"}'
Voice Wake + 대화 모드
- Voice Wake와 대화 모드는 설정에서 사용할 수 있습니다.
- iOS는 백그라운드 오디오를 일시 중지할 수 있으므로, 앱이 활성 상태가 아닐 때 음성 기능은 최선 노력 기반으로 동작합니다.
일반적인 오류
NODE_BACKGROUND_UNAVAILABLE: iOS 앱을 포그라운드로 가져오세요 (canvas/camera/screen 명령에는 포그라운드가 필요함).A2UI_HOST_NOT_CONFIGURED: 게이트웨이가 Canvas 호스트 URL을 광고하지 않았습니다. 게이트웨이 설정에서canvasHost를 확인하세요.- 페어링 프롬프트가 나타나지 않음:
openclaw devices list를 실행하고 수동으로 승인하세요. - 재설치 후 재연결 실패: 키체인 페어링 토큰이 삭제되었습니다. 노드를 다시 페어링하세요.