打破桌面边界
前面十五篇,OpenClaw 的能力都发生在一台机器上:Gateway 在跑,Agent 在这台机器的文件系统里读写,浏览器在这台机器上运行,工具调用的结果回到这台机器的进程。
这已经很有用了,但还差一个维度:物理世界的感知与控制。
你的手机有摄像头、有 GPS、有传感器,能发通知。你的树莓派连着传感器、跑着各种后台服务。你的第二台电脑上有另一套开发环境。这些设备各自有能力,但它们和 Agent 之间是割裂的——你没法让 Agent 直接拍一张手机里的照片,没法让它检查树莓派上某个服务的状态,没法让它把一张实时仪表盘渲染到你面前的手机屏幕上。
设备节点(Nodes)系统就是打通这个割裂的机制。
节点是什么,不是什么
理解节点,最重要的是先搞清楚它不是什么:节点不是 Gateway,不跑 Agent,不处理消息。
节点是一个外围设备(peripheral),它连上 Gateway 的 WebSocket,用 role: "node" 身份声明自己,然后向 Gateway 公告"我能做哪些事"。Agent 需要用到某个能力时,Gateway 把工具调用路由到对应的节点,节点在本地执行,把结果返回给 Gateway,再返回给 Agent。
消息仍然在 Gateway 上处理,AI 推理仍然在 Gateway 那台机器上运行。节点只负责执行 Gateway 发给它的具体指令,是 Agent 延伸出去的"手脚",而不是一个独立的 AI 实例。
一个典型的多节点部署:Mac Mini 跑 Gateway(也自动注册为一个 macOS 节点);iPhone 配对为 iOS 节点;树莓派通过 SSH 隧道接入,注册为无头 Linux 节点。你在 Telegram 发一条消息,Agent 可以同时让 iPhone 拍一张照片、让树莓派检查服务状态、把结果渲染到 Mac Mini 的屏幕上,综合之后回复你——全程在你发出消息之后的几秒内完成,不需要你打开任何 App,不需要手动操作任何设备。
配对流程:安全门卫
每一个节点接入 Gateway 之前,都必须完成设备配对——一个明确的人工审批步骤,这是 Gateway 对陌生设备连接的唯一安全门。
移动设备(iOS / Android)的配对流程是这样的:
在 Gateway 所在机器上运行 openclaw nodes pair --channel telegram(或任何其他通道),Gateway 生成一个 setup code——一段 base64 编码的 JSON,包含 Gateway 的 WebSocket 地址和一个一次性使用的 bootstrap token,通过消息通道发给你的手机。
在手机上打开 OpenClaw App,进入 Settings → Gateway,粘贴 setup code。App 解析出地址和 bootstrap token,建立 WebSocket 连接,发送 role: "node" 的握手请求。
这时 Gateway 侧出现一个待审批的配对请求,你在 Gateway 机器上运行:
openclaw devices list # 查看待审批请求openclaw devices approve <requestId> # 批准
批准后,App 收到一个持久化的设备 token,存入 iOS Keychain 或 Android Keystore。以后每次 App 重启,用这个 token 直接完成认证,不需要重复配对流程——除非你删除并重装 App,Keychain 里的 token 会随之清除,届时需要重新配对。
headless 节点(树莓派、服务器、WSL2)的配对略有不同。在节点机器上运行:
openclaw node run --display-name "RaspberryPi"如果 Gateway 绑定在 loopback(默认),远程节点无法直接连接,需要先建立 SSH 隧道:
# 在节点机器上:把本地 18790 转发到 Gateway 的 18789ssh -N -L 18790:127.0.0.1:18789 user@gateway-host# 另一个终端:export OPENCLAW_GATEWAY_TOKEN="<gateway.auth.token的值>"openclaw node run --host 127.0.0.1 --port 18790 --display-name "RaspberryPi"
Gateway 侧同样出现配对请求,openclaw devices approve <requestId> 批准后节点上线。节点的身份信息(ID、token、display name、Gateway 连接信息)持久化到节点机器的 ~/.openclaw/node.json,下次启动直接读取,无需重新配对。
有一个细节值得注意:openclaw nodes pending/approve/reject 和 openclaw devices pending/approve/reject 是两个不同的命令,管理的是两套不同的数据结构。devices 管理的是 WebSocket 握手层的设备身份(决定连接是否被允许建立);nodes 管理的是 Gateway 应用层的节点配对记录(决定节点的能力是否被信任)。两者都批准,节点才算真正可用。
能力声明与路由
节点连上 Gateway 之后,立刻发送一个 declare capabilities 消息,列出这个设备能执行的所有命令。Gateway 把这份能力清单缓存起来,用于两件事:工具列表生成(当 Agent 请求工具时,Gateway 把所有已连接节点的能力合并成工具列表的一部分);路由决策(工具调用来了,Gateway 根据能力清单找到能执行这个命令的节点,通过 node.invoke 下发)。
能力声明不是写死的,而是基于节点的实际运行时状态动态生成:iOS App 检测到摄像头权限被拒绝,camera.* 系列命令就不出现在能力声明里;Android App 检测到位置权限未授权,location.get 不出现。Gateway 看到的能力清单,就是节点当前真实可用的能力,不存在"声明了但实际调用会失败"的情况。
当多个节点声明了相同的能力时(比如你有两台 iPhone 都配对了),Agent 通过 --node <idOrNameOrIp> 参数指定目标节点,或者用 gateway.nodes.defaultNode 配置全局默认节点。不指定时,Gateway 选择最近一次活跃的节点。
macOS 节点:菜单栏里的隐形 Agent
如果你在 Mac 上跑 OpenClaw,macOS App(代号 Clawdis,项目里也常叫"菜单栏 App")安装后自动注册为一个 macOS 节点,不需要额外的配对操作。
macOS 节点的核心能力分为五类。
系统控制:system.run 执行本地 Shell 命令,相当于把 Mac 的终端能力暴露给 Agent;system.notify 推送系统通知,支持 passive(静默通知)、active(标准通知)、timeSensitive(突破勿扰模式的紧急通知)三个优先级;system.presence 报告系统状态(屏幕是否锁定、当前活跃 App、空闲时间)。
Canvas 渲染:在 macOS App 的 WebView 上渲染 Agent 指定的 HTML/SVG/JavaScript 内容,支持实时更新。Agent 可以用 canvas.eval 执行 JavaScript,读取当前 Canvas 的状态;用 canvas.url 把 WebView 导航到指定网页;用 canvas.snapshot 截图当前 Canvas 内容。
摄像头与屏幕:camera.snap 拍摄一张静态照片;screen.record 录制屏幕视频(受 macOS TCC 权限控制,首次使用需要在系统设置里授权 Screen Recording 权限)。
语音唤醒(Voice Wake):Swabble 是 OpenClaw 仓库里的一个独立子项目(Swabble/ 目录),实现了低功耗的本地唤醒词检测,唤醒词是 "Hey Claw"。Swabble 以 LaunchAgent 方式常驻后台,消耗极少的 CPU,检测到唤醒词后立即激活 macOS App 的 Push-to-Talk 覆盖层,你可以直接说话,语音被转录为文字后发给 Agent。
Push-to-Talk(PTT)覆盖层:一个半透明的悬浮窗,在任何 App 之上显示。按住键盘快捷键(默认 Fn 长按)激活,松开时把录音发给 Agent。Talk Mode 是更持续的模式:激活后不需要按键,App 持续监听,自动检测语音结束并发送。
system.run 的执行受到 Exec Approvals 机制的管控,配置在 macOS App 的 Settings → Exec Approvals 里,支持三种模式:ask(每次执行前弹窗确认)、allowlist(只允许执行预先批准的命令列表)、full(所有命令免审批直接执行)。对于陌生命令或参数,建议用 ask 或 allowlist 模式,防止 Agent 被 Prompt Injection 劫持后执行意外命令。
iOS 节点:口袋里的摄像头与 Canvas
iOS App 的核心能力与 macOS 高度重叠,但有几处 iOS 特有的功能。
camera.snap 和 camera.video 拍摄照片和视频。iOS 严格的后台限制意味着这两个命令要求 App 在前台运行;如果 App 在后台被调用,返回 NODE_BACKGROUND_UNAVAILABLE 错误,Agent 应该先发一条通知提醒用户把 App 切到前台。
location.get 获取设备当前 GPS 坐标,返回纬度、经度和精度半径。权限需要用户在 iOS 系统设置里手动授予"始终允许"或"使用期间允许"(后者在 App 后台时无法获取位置)。
notification.send 在设备上推送本地通知,支持标题、正文、sound 和 badge 计数。这是 Agent 主动打扰用户的最直接方式——定时任务完成、监控触发告警、日历事件提醒,都可以通过这个命令以原生 iOS 通知的形式出现在你的锁屏上。
screen.record 录制 App 内部的 Canvas WebView 内容(不是整个 iOS 屏幕录制,那需要系统级权限,iOS 不支持在后台授予给第三方 App)。
语音唤醒在 iOS 上通过 App 内的 SpeechRecognition API 实现,说 "Hey Claw" 激活;Apple Watch 配套 App 在表盘显示 Gateway 健康状态,并支持直接在手表上发送预设快捷消息。
Bonjour 发现是 iOS 节点找到 Gateway 的默认机制:iOS App 监听 _openclaw-gw._tcp 服务,在局域网内自动发现正在运行 Gateway 的机器,通常不需要手动输入 IP 地址。如果 Gateway 在 Tailnet 上,Tailscale 的 DNS-SD 实现让 Bonjour 发现跨越物理网段,仍然自动找到。
Android 节点:更丰富的设备命令
Android 节点的基础能力(Canvas、摄像头、屏幕录制、通知)与 iOS 类似,但 Android 系统提供了更多设备级 API,让 Android 节点额外支持一批 iOS 上没有的命令。
device.notifications.list 读取 Android 通知栏里的所有通知,包括来自其他 App 的通知——这需要授予 Notification Access 权限。Agent 可以用这个能力"读取手机里有没有重要通知",而不需要你手动查看。
device.sms.list 读取短信收件箱,device.contacts.search 搜索联系人,device.calendar.events 读取日历事件,device.photos.recent 获取最近的相册照片,device.motion.get 获取加速度计和陀螺仪数据。这些能力在 iOS 上因为沙盒限制无法实现,Android 的权限模型让它们成为可能。
device.app.update 触发系统应用更新检查,适合跑在 Agent 定时任务里的"帮我看看有没有需要更新的 App"场景。
Android 节点的配对发现支持两种方式:同 iOS 一样的 Bonjour 局域网发现,以及 Tailscale unicast DNS-SD(Android 没有原生 mDNS 支持,需要通过 Tailscale 的 DNS 解析来发现 Tailnet 上的 Gateway)。如果两种方式都不可用,手动输入 Gateway 的 IP:Port 是最后的回退选项。
Android App 的前台服务(Foreground Service)确保节点连接在 App 被切到后台后仍然维持。这和 iOS 的情况不同:iOS 的后台连接会被系统在一段时间后终止,Android 的前台服务可以持续运行,系统通知栏里会出现一个持久的"OpenClaw 节点运行中"通知。
headless 节点:把任意 Linux 机器变成节点
任何能运行 Node.js 22+ 的 Linux 机器都可以注册为 headless 节点,不需要 GUI,不需要摄像头。
headless 节点的主要价值是把 system.run 暴露给 Agent——让 Agent 能在那台机器上执行任意 Shell 命令。一台树莓派接入成 headless 节点后,Agent 可以检查它的服务状态、读取传感器数据、重启某个 systemd 服务,完全不需要 SSH 的手动介入。
节点作为 systemd 服务安装,开机自启:
openclaw node install \--host <gateway-host> \--port 18789 \--display-name "RaspberryPi-Sensor"systemctl --user enable openclaw-nodesystemctl --user start openclaw-node
headless 节点的能力清单由平台类型决定:已知平台(macOS、Linux、Windows)有各自的默认能力白名单;未知平台用保守的默认白名单,排除 system.run 和 system.which。如果你需要为未知平台启用这些命令,在 Gateway 配置里明确声明:
{gateway: {nodes: {allowCommands: ["system.run", "system.which"],}}}
Exec Approvals 在 headless 节点上通过 ~/.openclaw/exec-approvals.json 管理,格式是命令路径到审批策略的映射:always-allow(无需询问直接执行)、always-deny(永远拒绝)、ask(每次执行前通过 Gateway 推送通知请求确认)。从 Gateway 侧批量管理白名单:
openclaw approvals allowlist add \--node "RaspberryPi-Sensor" \"/usr/bin/systemctl"
把 exec 绑定到节点
一个高级配置是把 Agent 的 exec 工具默认路由到某个特定节点执行,而不是在 Gateway 宿主机上执行:
openclaw config set tools.exec.host nodeopenclaw config set tools.exec.security allowlistopenclaw config set tools.exec.node "RaspberryPi-Sensor"
配置后,Agent 调用 exec 工具时,命令在树莓派上运行而不是 Mac 上。这让你可以在一台机器上对话、在另一台机器上执行——把高性能机器做 AI 推理,把低功耗边缘设备做实际执行,职责分离。
tools.exec.host 支持三个值:local(默认,在 Gateway 宿主机上执行)、node(路由到指定节点)、docker(在 Docker 沙箱里执行)。三者可以在配置里为不同 Agent 分别设置,不需要全局切换。
实用工具命令
节点系统有一套简洁的管理命令,日常运维用得到:
openclaw nodes status # 所有节点的在线/离线状态openclaw nodes describe --node <id> # 特定节点的详细信息和能力清单openclaw nodes run --node <id> -- <命令> # 在节点上执行命令(临时测试)openclaw nodes notify --node <id> --title "消息标题" --body "内容" # 推送通知openclaw nodes invoke \ # 直接调用节点命令--node <id> \--command canvas.snapshot \--params '{"maxWidth":900,"format":"jpeg"}'
openclaw nodes run 和 openclaw nodes invoke 的区别:前者是 Shell 命令执行(走 system.run),后者是任意节点命令调用(包括 canvas、camera 等非 Shell 命令)。两者都受 Exec Approvals 管控。
小结
设备节点是 OpenClaw 从"文字对话工具"变成"物理世界操作者"的关键跃升。它的设计极其克制:节点不跑 AI、不处理消息、不做任何 Gateway 该做的事,只专注于"暴露设备能力、执行 Gateway 的指令"。配对流程的强制审批保证了没有陌生设备能悄悄接入。Exec Approvals 的三级控制(always-allow / allowlist / ask)在便利性和安全性之间提供了明确可调的旋钮。
从用户体验的角度看,这套系统实现了一件极具价值的事:你的 AI 助手从一个"只能在那台电脑的命令行里做事"的工具,变成了能够"看到你手机摄像头里的世界、感知你的位置、控制你所有设备"的真正个人助理。
下一篇,我们从攻击者的视角审视这个系统——安全架构、沙箱边界、Prompt Injection,以及 ClawHub 供应链攻击的完整分析。
源码参考:src/nodes/ · apps/ios/ · apps/android/ · Swabble/ · docs/nodes/ · docs/platforms/ios.md · docs/platforms/android.md基于 commit bf6ec64f 版本
夜雨聆风