iOS 应用(节点模式)

状态:内部预览。iOS 应用目前尚未公开分发。

功能概述

  • 通过 WebSocket 连接 Gateway(局域网或 tailnet)。
  • 暴露节点能力:Canvas、屏幕截图、相机拍摄、位置、Talk mode、语音唤醒。
  • 接收 node.invoke 命令并上报节点状态事件。

前提条件

  • 另一台设备上运行着 Gateway(macOS、Linux 或 Windows WSL2)。
  • 网络可达:
    • 同一局域网通过 Bonjour,或者
    • 通过 Tailnet 使用 unicast DNS-SD(示例域名:openclaw.internal.),或者
    • 手动填写主机/端口(兜底方案)。

快速开始(配对 + 连接)

  1. 启动 Gateway:
openclaw gateway --port 18789
  1. 在 iOS 应用中,打开 Settings,选择一个已发现的 Gateway(或开启 Manual Host 手动输入主机/端口)。

  2. 在 Gateway 主机上批准配对请求:

openclaw devices list
openclaw devices approve <requestId>
  1. 验证连接:
openclaw nodes status
openclaw gateway call node.list --params "{}"

正式版的 Relay 推送

正式分发的 iOS 构建使用外部推送 relay,而不是把原始 APNs token 暴露给 Gateway。

Gateway 端配置:

{
  gateway: {
    push: {
      apns: {
        relay: {
          baseUrl: "https://relay.example.com",
        },
      },
    },
  },
}

整体流程:

  • iOS 应用通过 App Attest 和应用收据向 relay 注册。
  • relay 返回一个不透明的 relay handle 和一个注册范围的发送授权。
  • iOS 应用获取已配对 Gateway 的身份信息并包含在 relay 注册中,这样注册就被委托给了那个特定的 Gateway。
  • 应用通过 push.apns.register 将 relay 注册信息转发给已配对的 Gateway。
  • Gateway 使用存储的 relay handle 进行 push.test、后台唤醒和唤醒推送。
  • Gateway 的 relay base URL 必须与正式/TestFlight iOS 构建中内置的 relay URL 一致。
  • 如果应用后来连接到不同的 Gateway 或使用了不同 relay base URL 的构建,会刷新 relay 注册而非复用旧绑定。

在这种模式下 Gateway 不需要

  • 全局 relay token。
  • 用于正式/TestFlight relay 发送的直接 APNs 密钥。

运维流程:

  1. 安装正式/TestFlight iOS 构建。
  2. 在 Gateway 上设置 gateway.push.apns.relay.baseUrl
  3. 将应用与 Gateway 配对,等待连接完成。
  4. 应用在获得 APNs token、operator 会话连接成功且 relay 注册完成后,自动发布 push.apns.register
  5. 之后 push.test、重连唤醒和唤醒推送就能使用已存储的 relay 注册了。

兼容性说明:

  • OPENCLAW_APNS_RELAY_BASE_URL 仍可作为 Gateway 的临时环境变量覆盖。

认证与信任流程

relay 的存在是为了实现两个直接在 Gateway 上放 APNs 无法满足的约束:

  • 只有通过 Apple 正式分发的 OpenClaw iOS 构建才能使用托管 relay。
  • Gateway 只能为与该 Gateway 配对过的 iOS 设备发送 relay 推送。

逐跳拆解:

  1. iOS 应用 -> Gateway

    • 应用先通过标准 Gateway 认证流程与 Gateway 配对。
    • 由此获得经认证的 node 会话和 operator 会话。
    • 通过 operator 会话调用 gateway.identity.get
  2. iOS 应用 -> relay

    • 应用通过 HTTPS 调用 relay 注册接口。
    • 注册包含 App Attest 证明和应用收据。
    • relay 验证 bundle ID、App Attest 证明和 Apple 收据,且要求正式/生产分发路径。
    • 这就是为什么本地 Xcode/开发构建无法使用托管 relay。本地构建虽然有签名,但不满足 relay 要求的正式 Apple 分发证明。
  3. Gateway 身份委托

    • 注册前,应用通过 gateway.identity.get 获取已配对 Gateway 的身份信息。
    • 应用将该 Gateway 身份包含在 relay 注册载荷中。
    • relay 返回的 relay handle 和发送授权都委托给了该 Gateway 身份。
  4. Gateway -> relay

    • Gateway 存储来自 push.apns.register 的 relay handle 和发送授权。
    • push.test、重连唤醒和唤醒推送时,Gateway 用自己的设备身份签名发送请求。
    • relay 同时验证存储的发送授权和 Gateway 签名,与注册时委托的 Gateway 身份进行比对。
    • 另一个 Gateway 即使获得了该 handle 也无法复用这个注册。
  5. relay -> APNs

    • relay 持有正式构建的生产 APNs 凭证和原始 APNs token。
    • 对于 relay 支持的正式构建,Gateway 永远不会存储原始 APNs token。
    • relay 代表已配对的 Gateway 向 APNs 发送最终推送。

这个设计的初衷:

  • 让生产 APNs 凭证远离用户的 Gateway。
  • 避免在 Gateway 上存储正式构建的原始 APNs token。
  • 只允许正式/TestFlight OpenClaw 构建使用托管 relay。
  • 防止一个 Gateway 向属于其他 Gateway 的 iOS 设备发送唤醒推送。

本地/手动构建仍然走直连 APNs。如果你在没有 relay 的情况下测试这些构建,Gateway 还需要直连 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(局域网)

Gateway 在 local. 上广播 _openclaw-gw._tcp。iOS 应用会自动列出这些服务。

Tailnet(跨网络)

如果 mDNS 不可用,使用 unicast DNS-SD zone(选一个域名,例如 openclaw.internal.)加 Tailscale split DNS。 CoreDNS 配置示例见 Bonjour

手动填写主机/端口

在 Settings 中,开启 Manual Host 并输入 Gateway 主机 + 端口(默认 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/"}'

说明:

  • Gateway canvas host 提供 /__openclaw__/canvas//__openclaw__/a2ui/
  • 它由 Gateway HTTP 服务器提供(端口与 gateway.port 相同,默认 18789)。
  • iOS 节点在连接时,如果有 canvas host 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"}'

语音唤醒 + Talk mode

  • 语音唤醒和 talk mode 在 Settings 中可用。
  • iOS 可能在后台挂起音频;应用不在前台时,语音功能只能尽力保证。

常见错误

  • NODE_BACKGROUND_UNAVAILABLE:把 iOS 应用切到前台(canvas/相机/屏幕命令需要前台)。
  • A2UI_HOST_NOT_CONFIGURED:Gateway 没有广播 canvas host URL;检查 Gateway 配置 中的 canvasHost
  • 配对提示一直不出现:运行 openclaw devices list 手动批准。
  • 重装后重连失败:Keychain 中的配对 token 被清除了,需要重新配对。

相关文档