普通视图

发现新文章,点击刷新页面。
昨天 — 2026年3月14日首页

万字解析 OpenClaw 源码架构-跨平台应用之MacOS 应用

作者 毛骗导演
2026年3月14日 17:30

菜单栏控制界面简介

本文面向 macOS 菜单栏控制界面,系统性阐述菜单栏图标功能、状态指示器与快捷操作面板的设计与实现。内容覆盖菜单项组织结构、上下文菜单与系统托盘集成、应用生命周期管理、内存优化与系统事件响应、用户交互设计、键盘快捷键支持与无障碍功能,以及菜单栏自定义选项、主题切换与通知配置方法。目标是帮助开发者与使用者全面理解该界面的架构与使用方式。

项目结构

菜单栏控制界面主要由以下模块构成:

  • 应用入口与场景管理:负责菜单栏图标、状态栏按钮外观、菜单打开/关闭事件处理、悬浮 HUD 与聊天面板的协调。
  • 菜单内容与上下文菜单:提供主菜单、会话注入、设备节点展示、用量与计费信息等动态内容。
  • 图标渲染与状态指示:基于状态生成菜单栏图标,包含动画与徽章提示。
  • 面板与悬浮窗:提供无边框面板承载聊天,以及悬停 HUD 快速预览工作状态。
  • 设置与自定义:提供多标签设置窗口,支持权限、通道、语音唤醒、实例、会话、Cron、技能、调试与关于等。
graph TB
subgraph "应用层"
App["OpenClawApp<br/>MenuBar.swift"]
Delegate["AppDelegate<br/>MenuBar.swift"]
Settings["SettingsRootView<br/>SettingsRootView.swift"]
end
subgraph "菜单与上下文"
MenuContent["MenuContent<br/>MenuContentView.swift"]
Sessions["MenuSessionsInjector<br/>MenuSessionsInjector.swift"]
ContextCard["MenuContextCardInjector<br/>MenuContextCardInjector.swift"]
end
subgraph "图标与状态"
StatusLabel["CritterStatusLabel<br/>CritterStatusLabel.swift"]
IconState["IconState<br/>IconState.swift"]
IconRenderer["CritterIconRenderer<br/>CritterIconRenderer.swift"]
end
subgraph "面板与HUD"
HoverHUD["HoverHUDController<br/>HoverHUD.swift"]
PanelFactory["OverlayPanelFactory<br/>OverlayPanelFactory.swift"]
WebChat["WebChatManager<br/>WebChatManager.swift"]
end
App --> MenuContent
App --> StatusLabel
StatusLabel --> IconRenderer
MenuContent --> Sessions
MenuContent --> Settings
App --> HoverHUD
HoverHUD --> PanelFactory
HoverHUD --> WebChat
App --> WebChat
App --> Delegate

核心组件

  • OpenClawApp:应用主体,定义菜单栏场景(MenuBarExtra),绑定状态与更新控制器,处理菜单呈现状态变化与悬浮 HUD 抑制策略。
  • MenuContent:主菜单视图,包含连接状态切换、心跳发送、浏览器控制、相机授权、执行审批模式、画布开关、语音唤醒、仪表盘与聊天入口、调试菜单、设置与关于、退出等。
  • CritterStatusLabel:状态栏图标组件,根据状态渲染动画与徽章,支持闪烁、摆动、耳部动画与庆祝效果。
  • CritterIconRenderer:图标绘制引擎,生成模板化图标,支持身体、耳朵、腿部、眼睛与徽章绘制,并进行抗锯齿与透明度处理。
  • IconState:图标状态模型,区分空闲、主要工作、其他工作与覆盖状态,提供徽章符号与显著性。
  • MenuSessionsInjector:菜单注入器,动态向菜单插入会话列表、用量统计、计费图表与设备节点,支持宽度缓存与后台刷新。
  • HoverHUDController:悬停 HUD 控制器,提供悬停延时显示、面板悬停检测、点击展开聊天、全局点击外区域自动隐藏等功能。
  • OverlayPanelFactory:无边框面板工厂,统一创建、动画呈现、帧调整与隐藏逻辑。
  • WebChatManager:聊天面板管理器,支持窗口与面板两种呈现模式,提供锚点定位与可见性回调。
  • SettingsRootView:设置根视图,多标签页组织,支持权限监控、调试标签按需显示、Nix 模式提示等。

架构总览

菜单栏控制界面采用“场景驱动 + 动态注入 + 状态驱动”的架构:

  • 场景驱动:通过 MenuBarExtra 定义菜单栏入口,状态绑定驱动图标与菜单行为。
  • 动态注入:MenuSessionsInjector 在菜单打开时注入会话、用量、计费与设备节点,保持菜单宽度稳定与后台刷新。
  • 状态驱动:IconState 与 AppState 决定图标状态、动画与菜单项可用性;HoverHUD 与 WebChatManager 协调面板与 HUD 的显示与隐藏。
sequenceDiagram
participant 用户 as "用户"
participant 状态栏 as "状态栏按钮"
participant 应用 as "OpenClawApp"
participant 菜单 as "MenuContent"
participant 注入器 as "MenuSessionsInjector"
participant HUD as "HoverHUDController"
participant 面板 as "WebChatManager"
用户->>状态栏 : 左键点击
状态栏->>应用 : 触发左键回调
应用->>面板 : 切换聊天面板
面板-->>应用 : 可见性变更回调
应用->>HUD : 抑制悬浮显示
用户->>状态栏 : 右键点击
状态栏->>应用 : 触发右键回调
应用->>应用 : 绑定 isMenuPresented = true
应用->>菜单 : 打开菜单
菜单->>注入器 : 菜单即将打开
注入器->>注入器 : 缓存/刷新数据
注入器-->>菜单 : 注入会话/用量/设备
用户->>状态栏 : 悬停
状态栏->>HUD : 悬停进入
HUD->>HUD : 延时显示
HUD-->>用户 : 展示悬浮 HUD
用户->>HUD : 点击
HUD->>面板 : 展开聊天面板

详细组件分析

菜单栏图标与状态指示器

  • 图标生成:CritterIconRenderer 使用位图与路径绘制,确保 Retina 下清晰锐利;支持身体、耳朵、腿部、眼睛与徽章绘制,并启用模板渲染以适配浅色/深色模式。
  • 状态映射:IconState 决定徽章符号与显著性,Idle、WorkingMain、WorkingOther、Overridden 四种状态;BadgeProminence 控制徽章尺寸与对比度。
  • 动画与闪烁:CritterStatusLabel 管理眨眼、摆动、耳部与腿部动画参数,结合 AppState 控制是否启用动画与睡眠状态。
classDiagram
class IconState {
+idle
+workingMain(ActivityKind)
+workingOther(ActivityKind)
+overridden(ActivityKind)
+badgeSymbolName : String
+badgeProminence : BadgeProminence
+isWorking : Bool
}
class CritterIconRenderer {
+makeIcon(blink, legWiggle, earWiggle, earScale, earHoles, eyesClosedLines, badge) NSImage
-drawBody()
-drawFace()
-drawBadge()
}
class CritterStatusLabel {
+isPaused : Bool
+isSleeping : Bool
+isWorking : Bool
+earBoostActive : Bool
+blinkTick : Int
+sendCelebrationTick : Int
+gatewayStatus
+animationsEnabled : Bool
+iconState : IconState
}
IconState --> CritterIconRenderer : "决定徽章与状态"
CritterStatusLabel --> IconState : "消费状态"
CritterStatusLabel --> CritterIconRenderer : "生成图标"

主菜单与上下文菜单

  • 主菜单结构:包含连接状态切换、心跳发送、浏览器控制、相机授权、执行审批模式、画布开关、语音唤醒、仪表盘、聊天、Talk Mode、设置、调试菜单、关于与退出。
  • 上下文菜单注入:MenuSessionsInjector 在菜单打开时注入会话头、会话列表、用量与计费图表、设备节点与更多设备菜单,支持宽度缓存与后台刷新,避免频繁布局抖动。
  • 菜单项高亮:MenuItemHighlightColors 提供高亮与非高亮颜色方案,保证在选中状态下仍可读。
flowchart TD
Start(["菜单即将打开"]) --> InjectHeader["注入会话头部"]
InjectHeader --> CheckSnapshot{"有会话快照?"}
CheckSnapshot --> |是| InjectRows["注入会话行(排序/过滤)"]
CheckSnapshot --> |否| LoadingMsg["显示加载/断连消息"]
InjectRows --> InjectUsage["注入用量头部与行"]
InjectUsage --> InjectCost["注入计费图表子菜单"]
InjectCost --> InjectNodes["注入设备节点与更多设备"]
InjectNodes --> End(["完成"])
LoadingMsg --> End

悬浮 HUD 与聊天面板

  • 悬浮 HUD:HoverHUDController 提供悬停延时显示、面板悬停检测、点击展开聊天、全局点击外区域自动隐藏与动画过渡。
  • 聊天面板:WebChatManager 支持窗口与面板两种呈现模式,面板具备锚点定位与可见性回调,适配菜单栏按钮位置。
  • 面板工厂:OverlayPanelFactory 统一创建无边框面板、动画呈现与帧调整,保证跨屏幕与多分辨率兼容。
sequenceDiagram
participant 状态栏 as "状态栏按钮"
participant HUD as "HoverHUDController"
participant 工厂 as "OverlayPanelFactory"
participant 面板 as "WebChatManager"
状态栏->>HUD : 悬停进入
HUD->>HUD : 启动延时任务
HUD->>HUD : 延时后检查悬停状态
HUD->>工厂 : 创建面板并动画呈现
工厂-->>HUD : 面板可见
HUD->>面板 : 展示聊天面板(锚点定位)
用户->>HUD : 点击HUD
HUD->>面板 : 切换到聊天面板

设置与自定义

  • 多标签设置:SettingsRootView 提供通用、通道、语音唤醒、配置、实例、会话、Cron、技能、权限、调试与关于等标签页。
  • 权限监控:在权限标签页启用时,周期性刷新权限状态,便于用户确认授权。
  • 调试标签:仅在调试模式开启时显示,包含健康检查、心跳发送、远程隧道重置、日志与重启等调试能力。
  • Nix 模式提示:在 Nix 环境下显示配置与状态目录路径,便于用户识别。

通知与覆盖层

  • 通知覆盖层:NotifyOverlay 提供覆盖层弹窗,支持首次出现动画、窗口定位与自动隐藏,适合在菜单栏附近展示简短通知。
  • 通知生命周期:通过 dismiss 任务与窗口动画,确保覆盖层在合适时机消失且不影响菜单栏交互。

依赖关系分析

  • 组件耦合与内聚:
    • OpenClawApp 与 MenuContent 通过状态绑定强关联,确保 UI 与业务状态一致。
    • MenuSessionsInjector 与 ControlChannel、SessionLoader、NodesStore 解耦,通过观察与缓存机制降低菜单打开时的阻塞。
    • HoverHUDController 与 WebChatManager 通过回调与可见性状态解耦,避免直接耦合。
  • 外部依赖与集成点:
    • MenuBarExtraAccess 提供菜单栏额外访问能力。
    • Sparkle 更新器在签名条件下启用,否则使用禁用控制器。
    • 系统事件:全局鼠标按下监听用于 HUD 自动隐藏,窗口层级与集合行为确保面板始终可见且不抢夺焦点。
graph LR
OpenClawApp["OpenClawApp"] --> MenuContent["MenuContent"]
OpenClawApp --> HoverHUD["HoverHUDController"]
OpenClawApp --> WebChat["WebChatManager"]
MenuContent --> Sessions["MenuSessionsInjector"]
HoverHUD --> PanelFactory["OverlayPanelFactory"]
WebChat --> PanelFactory
OpenClawApp --> Sparkle["SparkleUpdaterController"]
OpenClawApp --> MBEA["MenuBarExtraAccess"]

性能考虑

  • 图标渲染优化:使用 36×36 像素位图作为 Retina 后备缓冲,避免缩放失真;禁用抗锯齿与模板渲染提升清晰度。
  • 菜单注入缓存:MenuSessionsInjector 缓存会话、用量与计费数据,限定刷新间隔,菜单打开时仅做增量更新与宽度缓存,减少布局抖动。
  • 异步与取消:所有网络与 IO 操作均使用 Task 并在菜单关闭或状态变化时及时取消,避免资源泄漏。
  • HUD 延时与动画:悬停延时与短时动画减少不必要的 UI 更新,全局事件监听仅在需要时安装。
  • 面板复用:WebChatManager 对面板控制器进行缓存,避免重复初始化带来的启动延迟。

macOS 应用

OpenClaw 的 macOS 应用位于 apps/macos 目录,采用 Swift Package Manager 组织多目标产物:菜单栏可执行程序、IPC 库、发现库、以及一个 CLI 工具。Swabble 作为语音唤醒与转写能力的核心模块被集成进来;同时通过 Sparkle 实现更新分发,Peekaboo 提供系统级自动化桥接能力。

graph TB
subgraph "macOS 应用包"
OC["OpenClaw 可执行程序"]
IPC["OpenClawIPC 库"]
DISC["OpenClawDiscovery 库"]
CLI["OpenClawMacCLI 可执行程序"]
end
subgraph "外部依赖"
SWABBLE["Swabble 核心与工具集"]
SPARKLE["Sparkle 更新框架"]
MBX["MenuBarExtraAccess 菜单栏扩展"]
SUBPROC["swift-subprocess 子进程"]
LOGGING["swift-log 日志"]
PEEK["Peekaboo 桥接"]
end
OC --> IPC
OC --> DISC
OC --> SWABBLE
OC --> SPARKLE
OC --> MBX
OC --> SUBPROC
OC --> LOGGING
OC --> PEEK
CLI --> DISC
CLI --> SWABBLE

核心组件

  • 菜单栏控制界面:基于 MenuBarExtraAccess 构建,提供快速入口与状态指示,支持与主应用交互。
  • 语音唤醒与转写:Swabble 提供唤醒词检测、音频缓冲转换、实时转写与会话存储。
  • WebChat 聊天界面:通过 OpenClawChatUI 集成,提供网页聊天体验并与后端协议对接。
  • 后台服务与 IPC:OpenClawIPC 提供跨进程通信能力,OpenClawDiscovery 负责设备/服务发现。
  • 更新与分发:Sparkle 驱动自动更新,配合签名与公证流程实现安全分发。
  • 系统集成:Peekaboo 桥接系统自动化能力,日志与子进程管理提升稳定性。

架构总览

下图展示 macOS 应用从启动到功能运行的关键路径:菜单栏入口触发主逻辑,Swabble 处理语音输入,IPC 与协议层连接后端,Sparkle 负责更新,Peekaboo 提供系统级能力。

graph TB
MB["菜单栏入口<br/>MenuBarExtraAccess"] --> APP["OpenClaw 主程序"]
APP --> WAKE["Swabble 语音唤醒<br/>WakeWordGate"]
WAKE --> PIPE["音频管线<br/>SpeechPipeline"]
PIPE --> BUF["缓冲转换<br/>BufferConverter"]
BUF --> TR["转写与会话<br/>TranscriptsStore"]
APP --> IPC["OpenClawIPC"]
IPC --> PROTO["OpenClaw 协议层"]
APP --> UI["WebChat 界面<br/>OpenClawChatUI"]
APP --> SPK["Sparkle 更新"]
APP --> PEE["Peekaboo 桥接"]
APP --> LOG["日志与监控"]

详细组件分析

菜单栏控制界面

  • 设计目标:在菜单栏提供最小化占用的控制入口,承载状态显示与常用操作。
  • 关键点:使用 MenuBarExtraAccess 构建,结合主程序状态动态更新菜单项,避免阻塞主线程。
  • 交互流程:点击菜单项触发主程序逻辑,如打开 WebChat、切换录音状态或查看健康状态。
sequenceDiagram
participant U as "用户"
participant MB as "菜单栏"
participant APP as "OpenClaw 主程序"
U->>MB : 点击菜单图标
MB->>APP : 触发菜单事件
APP->>APP : 更新状态/打开界面
APP-->>U : 展示结果/反馈

语音唤醒功能

  • 唤醒词检测:SwabbleKit 的 WakeWordGate 提供轻量级唤醒词门控,降低误触发。
  • 音频管线:SpeechPipeline 负责持续采集与预处理,BufferConverter 将音频缓冲标准化以便后续处理。
  • 会话存储:TranscriptsStore 记录转写片段,支持回放与上下文构建。
  • 命令行工具:CLI 提供 mic/list、mic/set、service/install 等命令,便于开发调试与自动化。
flowchart TD
Start(["开始监听"]) --> Detect["唤醒词检测"]
Detect --> |未触发| Wait["继续等待"]
Detect --> |触发| Pipeline["音频管线处理"]
Pipeline --> Convert["缓冲转换"]
Convert --> Transcribe["实时转写"]
Transcribe --> Store["会话存储"]
Store --> Notify["通知主程序"]
Wait --> Detect
Notify --> End(["结束一轮"])

WebChat 聊天界面

  • 集成方式:通过 OpenClawChatUI 提供网页聊天界面,与后端协议层对接实现消息收发。
  • 控制流:主程序负责初始化 UI、建立连接、转发用户输入与系统事件,保持界面响应性。
  • 适配策略:针对不同分辨率与主题模式进行布局与样式适配,确保一致的用户体验。
sequenceDiagram
participant U as "用户"
participant UI as "WebChat 界面"
participant IPC as "OpenClawIPC"
participant PROTO as "协议层"
U->>UI : 输入消息/发送
UI->>IPC : 发送消息请求
IPC->>PROTO : 转发至后端
PROTO-->>IPC : 返回响应
IPC-->>UI : 渲染消息/状态
UI-->>U : 展示结果

系统集成特性

  • 自动化桥接:Peekaboo 桥接系统自动化能力,支持与系统服务交互。
  • 日志与监控:swift-log 提供统一日志输出,便于问题定位与性能观测。
  • 子进程管理:swift-subprocess 管理外部进程生命周期,保证稳定性与可控性。
graph TB
APP["OpenClaw 主程序"] --> PEE["Peekaboo 桥接"]
APP --> LOG["swift-log 日志"]
APP --> SUB["swift-subprocess 子进程"]
PEE --> SYS["系统服务/自动化"]
LOG --> MON["监控与诊断"]
SUB --> EXT["外部工具/服务"]

依赖关系分析

  • 内部模块:OpenClaw 依赖 OpenClawIPC、OpenClawDiscovery、OpenClawChatUI、OpenClawProtocol 等内部产品。
  • 外部模块:Swabble 提供语音相关能力;Sparkle 负责更新;MenuBarExtraAccess 提供菜单栏扩展;Peekaboo 提供系统桥接;swift-log 与 swift-subprocess 提供日志与子进程能力。
  • 版本与平台:最低 macOS 版本要求在 Package 中声明,Swabble 对新版本 macOS 有明确可用性标注。
graph LR
OC["OpenClaw"] --> IPC["OpenClawIPC"]
OC --> DISC["OpenClawDiscovery"]
OC --> UI["OpenClawChatUI"]
OC --> PROTO["OpenClawProtocol"]
OC --> SWAB["Swabble"]
OC --> SPK["Sparkle"]
OC --> MBX["MenuBarExtraAccess"]
OC --> PEE["Peekaboo"]
OC --> LOG["swift-log"]
OC --> SUB["swift-subprocess"]

性能考虑

  • 低延迟唤醒:WakeWordGate 与 SpeechPipeline 应尽量减少预处理开销,避免阻塞主线程。
  • 缓冲与内存:BufferConverter 与 TranscriptsStore 需要合理设置缓冲大小与清理策略,防止内存膨胀。
  • 线程模型:遵循 Swift 并发模型,避免在主线程执行耗时任务,使用后台队列处理音频与网络。
  • I/O 优化:IPC 与协议层应批量处理消息,减少频繁的小数据包传输。
  • 日志级别:生产环境降低日志级别,仅保留关键信息,避免磁盘与 CPU 开销。

系统集成特性

macOS 相关实现主要集中在 apps/macos 工程中,采用多目标组织方式:

  • 可执行目标 OpenClaw:菜单栏应用主体
  • 库目标 OpenClawIPC、OpenClawDiscovery:跨进程通信与发现能力
  • CLI 目标 OpenClawMacCLI:命令行工具
  • 测试目标 OpenClawIPCTests:测试套件
graph TB
subgraph "macOS 工程"
A["OpenClaw<br/>菜单栏应用"]
B["OpenClawIPC<br/>IPC 库"]
C["OpenClawDiscovery<br/>发现库"]
D["OpenClawMacCLI<br/>CLI 工具"]
E["OpenClawIPCTests<br/>测试套件"]
end
subgraph "外部依赖"
S["Sparkle<br/>自动更新"]
M["MenuBarExtraAccess<br/>菜单栏扩展"]
L["Logging<br/>日志"]
P["Peekaboo<br/>桥接/自动化"]
end
A --> B
A --> C
A --> D
A --> S
A --> M
A --> L
A --> P
E --> B
E --> A
E --> C

核心组件

  • 权限管理器:统一处理各类系统权限的检查、请求与状态监控
  • 设置界面:集中展示与管理权限、位置访问模式、自动更新等
  • 后台服务与事件:LaunchAgent 生命周期、心跳与系统事件过滤
  • 自动更新:Sparkle 控制器、签名检测、发布脚本
  • 系统设置跳转:便捷打开系统隐私与安全设置

架构总览

下图展示 macOS 端系统集成的关键交互:菜单栏应用、权限管理、后台服务、自动更新与系统设置。

graph TB
subgraph "用户空间"
UI["菜单栏应用<br/>MenuBar.swift"]
SET["设置界面<br/>SettingsRootView.swift"]
PERM["权限管理器<br/>PermissionManager.swift"]
HELP["系统设置跳转<br/>SystemSettingsURLSupport.swift"]
end
subgraph "系统服务"
LA["LaunchAgent<br/>launchd.ts"]
SYS["系统权限/设置"]
UPD["Sparkle 更新<br/>make_appcast.sh"]
end
subgraph "外部库"
SPK["Sparkle"]
MBE["MenuBarExtraAccess"]
LOG["Logging"]
PBO["Peekaboo"]
end
UI --> PERM
UI --> SET
PERM --> SYS
SET --> HELP
UI --> LA
UI --> UPD
UI --> SPK
UI --> MBE
UI --> LOG
UI --> PBO

详细组件分析

权限管理与用户授权

  • 统一入口:PermissionManager 提供权限检查、请求与状态查询
  • 支持能力:通知、AppleScript、无障碍、屏幕录制、麦克风、语音识别、摄像头、位置
  • 交互策略:非交互模式仅返回当前状态;交互模式触发系统授权对话或引导至系统设置
  • 状态监控:PermissionMonitor 定时轮询并缓存状态,避免频繁调用系统 API
  • 系统设置跳转:针对不同权限类别提供便捷链接,快速打开系统隐私与安全设置
classDiagram
class PermissionManager {
+ensure(caps, interactive) [Capability : Bool]
+ensureNotifications(interactive) Bool
+ensureAppleScript(interactive) Bool
+ensureAccessibility(interactive) Bool
+ensureScreenRecording(interactive) Bool
+ensureMicrophone(interactive) Bool
+ensureSpeechRecognition(interactive) Bool
+ensureCamera(interactive) Bool
+ensureLocation(interactive) Bool
+status(caps) [Capability : Bool]
}
class PermissionMonitor {
+register()
+unregister()
+refreshNow()
-startMonitoring()
-stopMonitoring()
-checkStatus(force)
}
class SystemSettingsURLSupport {
+openFirst(urls)
}
PermissionManager --> SystemSettingsURLSupport : "打开系统设置"
PermissionMonitor --> PermissionManager : "轮询状态"

权限设置界面与位置访问

  • 集中式权限面板:显示各能力授权状态、一键请求、刷新按钮
  • 位置访问控制:支持关闭、使用期间、始终三种模式,并可选择精确位置
  • 用户体验:在切换模式后自动尝试授权,失败时引导至系统设置
flowchart TD
Start(["进入权限设置"]) --> ShowCaps["展示各能力状态"]
ShowCaps --> ChooseMode{"选择位置模式"}
ChooseMode --> |Off| Done["保持关闭"]
ChooseMode --> |WhileUsing/Always| Request["请求授权"]
Request --> Granted{"已授权?"}
Granted --> |是| Done
Granted --> |否| OpenPrefs["打开系统设置"]
OpenPrefs --> Revert["回滚到上一模式"]
Revert --> Done

后台服务机制与系统事件监听

  • LaunchAgent 管理:安装、停止、重启、修复引导,支持保留 umask 与节流
  • 心跳与系统事件:基于文件系统的事件队列,区分执行完成、定时任务等事件类型
  • 运行时事件桥接:通过运行时接口向系统发送通知
sequenceDiagram
participant User as "用户"
participant App as "菜单栏应用"
participant Daemon as "LaunchAgent"
participant FS as "系统事件文件"
participant Runner as "心跳运行器"
User->>App : 打开设置/触发动作
App->>Daemon : 安装/重启/停止
Daemon-->>FS : 写入系统事件
Runner->>FS : 轮询/读取事件
Runner->>Runner : 过滤执行完成/定时任务事件
Runner-->>App : 处理结果/触发后续动作

自动更新机制与发布流程

  • Sparkle 集成:根据签名状态启用/禁用自动更新控制器
  • 发布脚本:生成 appcast,嵌入发布说明,签名更新包
  • 版本与下载前缀:从 zip 文件名推断版本,支持预发布格式
sequenceDiagram
participant Dev as "开发者"
participant Script as "make_appcast.sh"
participant Sparkle as "Sparkle 工具"
participant Repo as "发布仓库"
Dev->>Script : 传入 zip 与密钥
Script->>Sparkle : generate_appcast
Sparkle-->>Script : 生成 appcast.xml
Script->>Repo : 写回 appcast.xml
Repo-->>Dev : 可用的更新源

系统启动项配置

  • LaunchAgent 安装:写入 plist,设置 KeepAlive、umask、节流间隔
  • 重启顺序:bootout -> unload -> 删除旧 plist -> 写新 plist -> bootstrap -> kickstart
  • attach-only 模式:禁用 LaunchAgent 写入,避免自动启动
flowchart TD
Start(["安装/重启 LaunchAgent"]) --> StopOld["bootout + unload 旧 Agent"]
StopOld --> Cleanup["删除旧 plist"]
Cleanup --> WriteNew["写入新 plist"]
WriteNew --> Bootstrap["bootstrap 新 Agent"]
Bootstrap --> Kickstart["kickstart -k"]
Kickstart --> Done(["完成"])

系统版本兼容性

  • 最低系统版本:macOS 15.0
  • 平台约束:Swift 包定义中指定最低版本
  • 权限 API 兼容:对较老版本进行降级处理(如屏幕录制)

系统通知集成、Spotlight 支持与快速查看

  • 系统通知:通过运行时接口发送系统通知,支持优先级与投递方式
  • Spotlight/快速查看:本仓库未提供直接实现,建议结合 Info.plist 中的使用说明描述与系统框架进行扩展(概念性说明)

依赖关系分析

  • 包依赖:Sparkle、MenuBarExtraAccess、Logging、Peekaboo 等
  • 目标耦合:OpenClaw 主目标依赖 IPC、Discovery、Kit、Swabble 等产品库
  • 测试依赖:测试目标依赖 IPC 与协议库
graph LR
OpenClaw["OpenClaw 目标"] --> IPC["OpenClawIPC"]
OpenClaw --> Discovery["OpenClawDiscovery"]
OpenClaw --> Kit["OpenClawKit"]
OpenClaw --> Protocol["OpenClawProtocol"]
OpenClaw --> Swabble["SwabbleKit"]
OpenClaw --> MBE["MenuBarExtraAccess"]
OpenClaw --> Subproc["Subprocess"]
OpenClaw --> Logging["Logging"]
OpenClaw --> Sparkle["Sparkle"]
OpenClaw --> Peekaboo["Peekaboo"]
OpenClaw --> PKit["PeekabooAutomationKit"]

性能考量

  • 权限轮询节流:PermissionMonitor 使用最小检查间隔,避免频繁调用系统 API
  • 后台服务稳定性:LaunchAgent 采用 KeepAlive 与节流参数,减少资源占用
  • 心跳事件过滤:仅处理必要事件,跳过空心跳与执行完成噪声
  • 日志与可观测性:引入 Logging,便于定位问题

应用打包与分发

围绕 macOS 打包的核心脚本与配置位于 scripts/ 与 apps/macos/ 目录中,CI 流程由 .github/workflows/ci.yml 驱动。下图展示与打包分发直接相关的文件与职责:

graph TB
subgraph "脚本层"
P["package-mac-app.sh<br/>构建与打包.app"]
S["codesign-mac-app.sh<br/>代码签名"]
N["notarize-mac-artifact.sh<br/>公证与贴签"]
D["create-dmg.sh<br/>制作 DMG"]
PD["package-mac-dist.sh<br/>打包 zip+DMG+公证"]
MA["make_appcast.sh<br/>生成 appcast.xml"]
BI["build_icon.sh<br/>生成.icns"]
SB["sparkle-build.ts<br/>版本映射工具"]
end
subgraph "应用定义"
PSW["apps/macos/Package.swift<br/>产品与资源声明"]
PMD["apps/macos/README.md<br/>打包与签名说明"]
end
subgraph "CI"
CI["ci.yml<br/>macOS 检查流水线"]
end
P --> S --> N --> D
P --> BI
P --> PSW
PD --> N
PD --> D
MA --> CI
SB --> P
CI --> PSW

核心组件

  • 应用包构建与装配:负责 Swift 产物构建、Info.plist 注入、资源复制、签名与 Sparkle 嵌入。
  • 代码签名:自动选择证书、注入权限、校验 Team ID、支持临时签名与时间戳策略。
  • 公证与贴签:提交 zip/dmg/pkg 至 Apple 公证服务,必要时对 app 与 DMG 进行贴签验证。
  • DMG 制作:生成带背景、图标布局与 Applications 快捷方式的最终分发镜像。
  • 更新通道:通过 Sparkle 生成 appcast.xml 并嵌入发布说明。
  • CI 集成:在 macOS runner 上执行 Swift 构建、测试与覆盖率检查。

架构总览

下图展示从源码到分发产物的端到端流程,包括本地开发与 CI 两条路径:

sequenceDiagram
participant Dev as "开发者/CI"
participant Build as "package-mac-app.sh"
participant Sign as "codesign-mac-app.sh"
participant Notarize as "notarize-mac-artifact.sh"
participant DMG as "create-dmg.sh"
participant Appcast as "make_appcast.sh"
Dev->>Build : 触发打包
Build->>Build : 构建 Swift 产物/复制资源/写入 Info.plist
Build->>Sign : 传入 .app 进行签名
Sign-->>Build : 返回签名结果
Build-->>Dev : 产出 dist/OpenClaw.app
Dev->>Notarize : 提交 zip/dmg/pkg 公证
Notarize-->>Dev : 返回公证状态/贴签
Dev->>DMG : 生成 DMG含背景与布局
DMG-->>Dev : 输出 .dmg
Dev->>Appcast : 生成 appcast.xml 并上传
Appcast-->>Dev : appcast.xml 就绪

组件详解

应用包结构与资源装配

  • 包结构:dist/OpenClaw.app/Contents 下包含 MacOS、Resources、Frameworks、Info.plist。
  • 资源复制:图标、设备模型、Textual 资源包、OpenClawKit 资源包等。
  • Info.plist 注入:设置 Bundle ID、版本号、构建号、Sparkle 更新地址与公钥、自动检查开关等。
  • 多架构合并:若构建多架构,使用 lipo 合并 Sparkle.framework 与主二进制。
flowchart TD
Start(["开始"]) --> Clean["清理旧 .app 目录"]
Clean --> Mkdir["创建 Contents/MacOS/Resources/Frameworks"]
Mkdir --> CopyPlist["复制 Info.plist 模板并写入键值"]
CopyPlist --> CopyBin["复制主二进制并处理多架构"]
CopyBin --> EmbedSparkle["复制并合并 Sparkle.framework"]
EmbedSparkle --> CopyRes["复制图标/模型/Textual/OpenClawKit 资源"]
CopyRes --> End(["完成"])

Info.plist 配置要点

  • 关键键值:
    • CFBundleIdentifier:用于签名与权限持久化
    • CFBundleShortVersionString:显示版本
    • CFBundleVersion:Sparkle 比较用的构建号(需为纯数字且单调递增)
    • OpenClawBuildTimestamp / OpenClawGitCommit:构建元数据
    • SUFeedURL / SUPublicEDKey:Sparkle 更新通道
    • SUEnableAutomaticChecks:自动检查开关
  • 版本映射:当使用日期型语义版本时,脚本通过工具计算 Sparkle 可归一化的构建号。

图标资源管理

  • 生成流程:从 .icon 资源导出多尺寸 PNG,再合成 .icns,放置于 Resources/OpenClaw.icns。
  • 脚本支持自定义目标路径与 Xcode 路径,便于在 CI 中复用。

代码签名流程与权限策略

  • 自动选择签名身份:优先 Developer ID Application,其次 Apple Distribution,再 Apple Development,最后首个可用。
  • 权限注入:为应用注入自动化、音频、相机、位置等权限键。
  • Team ID 校验:签名后遍历所有 Mach-O,确保与主包 Team ID 一致,避免加载失败。
  • 临时签名:允许使用 ad-hoc(-)签名,但会禁用 runtime 选项并导致 TCC 权限不持久。
  • 时间戳策略:根据证书类型自动启用或关闭时间戳。
flowchart TD
A["选择签名身份"] --> B{"身份为空?"}
B -- 是 --> C["尝试 Developer ID Application"]
C --> D{"找到?"}
D -- 否 --> E["尝试 Apple Distribution"]
E --> F{"找到?"}
F -- 否 --> G["尝试 Apple Development"]
G --> H{"找到?"}
H -- 否 --> I["使用首个可用身份或报错"]
B -- 否 --> J["使用指定身份"]
J --> K["注入权限与签名参数"]
K --> L["签名主二进制"]
L --> M["深度签名 Sparkle 框架"]
M --> N["签名其他 Frameworks/Dylibs"]
N --> O["签名 .app 包"]
O --> P{"Team ID 一致?"}
P -- 否 --> Q["报错并退出"]
P -- 是 --> R["完成"]

Gatekeeper 验证与公证

  • Gatekeeper:要求应用具备有效签名与可识别的 Team ID,且无未签名嵌入组件。
  • 公证:通过 notarytool 提交 zip/dmg/pkg,等待 Apple 审核通过后返回票据。
  • 贴签:对 DMG 与 app 进行 stapler 贴签,确保离线验证成功。
sequenceDiagram
participant Dev as "开发者"
participant Zip as "zip/dmg/pkg"
participant Notary as "Apple Notary Service"
participant Stapler as "stapler"
Dev->>Zip : 准备待公证产物
Dev->>Notary : 提交公证凭配置的凭据
Notary-->>Dev : 返回公证状态
alt 需要贴签
Dev->>Stapler : 对产物与 app 进行贴签
Stapler-->>Dev : 验证通过
end

DMG 制作与分发镜像

  • 功能:创建带背景、图标布局、Applications 快捷方式的 DMG,自动调整窗口大小与图标位置。
  • 可定制:窗口边界、图标尺寸、背景图、额外扇区等。
  • 验证:对最终 DMG 进行完整性校验。

更新通道与 appcast.xml

  • 生成:解析 zip 名称推断版本,生成 HTML 发布说明,调用 Sparkle 工具生成 appcast.xml。
  • 上传:将 appcast.xml 与 zip 一同发布至指定链接。
  • 依赖:需要 Sparkle 工具链在 PATH 中可用。

CI/CD 集成与自动化

  • macOS 检查:在单个 runner 上顺序执行 TS 测试、Swift lint/format、Swift 构建与测试。
  • 缓存:缓存 SwiftPM 依赖,提升重复构建速度。
  • 并发:macOS 并发作业数有限,合并为单一作业以提高队列利用率。

依赖关系分析

  • 脚本间耦合:
    • package-mac-app.sh 依赖 codesign-mac-app.sh 完成签名。
    • package-mac-dist.sh 串联 zip、公证与 DMG 制作。
    • make_appcast.sh 依赖 sparkle-build.ts 计算构建号。
  • 应用定义:
    • apps/macos/Package.swift 声明产品、依赖与资源复制规则,影响打包阶段的资源装配。
graph LR
P["package-mac-app.sh"] --> S["codesign-mac-app.sh"]
P --> PSW["apps/macos/Package.swift"]
PD["package-mac-dist.sh"] --> N["notarize-mac-artifact.sh"]
PD --> D["create-dmg.sh"]
MA["make_appcast.sh"] --> SB["sparkle-build.ts"]

性能与可靠性考量

  • 多架构构建:默认按当前架构构建,发布时建议统一为 arm64 x86_64,减少用户下载体积与兼容性问题。
  • 缓存策略:SwiftPM 缓存与 UI 构建缓存可显著缩短 CI 时间。
  • 公证等待:公证可能成为瓶颈,建议在 CI 中并行化其他任务,公证完成后集中处理贴签与 DMG 制作。
  • 资源复制:避免重复拷贝与权限变更,减少打包时间。

语音唤醒功能

语音唤醒功能在项目中的组织结构如下:

graph TB
subgraph "macOS 应用层"
A[VoiceWakeRuntime] -- "实时唤醒监听" --> B[VoiceWakeTester]
A -- "音频处理" --> C[AVAudioEngine]
A -- "识别结果" --> D[Speech.framework]
E[VoiceWakeOverlayController] -- "UI 展示" --> F[VoiceSessionCoordinator]
G[VoiceWakeForwarder] -- "消息转发" --> H[GatewayConnection]
end
subgraph "Swabble 核心层"
I[WakeWordGate] -- "唤醒词匹配" --> J[WakeWordSegment]
K[SwabbleKit] -- "跨平台支持" --> L[多平台复用]
end
subgraph "网关服务层"
M[voicewake.ts] -- "配置管理" --> N[voicewake.json]
O[GatewayRPC] -- "状态同步" --> P[WebSocket 广播]
end
subgraph "配置层"
Q[VoiceWakeSettings] -- "用户配置" --> R[全局唤醒词列表]
S[VoiceWakePreferences] -- "偏好设置" --> T[音质参数]
end
A --> I
G --> O
R --> M

核心组件

语音唤醒运行时 (VoiceWakeRuntime)

VoiceWakeRuntime 是整个语音唤醒系统的核心执行组件,负责:

  • 实时音频流处理:通过 AVAudioEngine 实时捕获和处理音频数据
  • 唤醒词检测:使用 WakeWordGate 进行精确的唤醒词匹配
  • 状态管理:维护识别状态、会话管理和错误处理
  • 资源控制:智能启动和停止音频引擎以节省系统资源

唤醒词门控 (WakeWordGate)

WakeWordGate 提供了高级的唤醒词匹配算法:

  • 时间感知匹配:基于语音段的时间戳进行精确匹配
  • 后触发间隔要求:确保唤醒词后有足够的时间间隔才触发
  • 多词支持:支持多个唤醒词及其别名
  • 文本规范化:自动处理大小写、重音符号等字符差异

音频处理管道

系统采用分层的音频处理架构:

flowchart TD
A[麦克风输入] --> B[AVAudioEngine 输入节点]
B --> C[音频缓冲区处理]
C --> D[RMS 声音级别计算]
D --> E[噪声过滤器]
E --> F[Speech.framework 识别]
F --> G[唤醒词匹配]
G --> H[触发事件]
I[音频质量监控] --> D
J[自适应阈值] --> E
K[静音检测] --> H

架构概览

语音唤醒系统的整体架构采用模块化设计,确保各组件间的松耦合和高内聚:

graph TB
subgraph "输入层"
A[麦克风设备] --> B[音频采集]
B --> C[音频格式转换]
end
subgraph "处理层"
C --> D[音频预处理]
D --> E[语音活动检测]
E --> F[实时识别]
F --> G[唤醒词匹配]
end
subgraph "控制层"
G --> H[状态管理]
H --> I[会话协调]
I --> J[UI 更新]
end
subgraph "输出层"
J --> K[语音反馈]
J --> L[消息转发]
J --> M[日志记录]
end
subgraph "配置层"
N[全局配置] --> O[本地设置]
O --> P[用户偏好]
end
P --> H

详细组件分析

语音唤醒运行时实现

VoiceWakeRuntime 采用了 Actor 模式确保线程安全:

classDiagram
class VoiceWakeRuntime {
-recognizer : SFSpeechRecognizer
-audioEngine : AVAudioEngine
-recognitionRequest : SFSpeechAudioBufferRecognitionRequest
-recognitionTask : SFSpeechRecognitionTask
-isCapturing : Bool
-noiseFloorRMS : Double
-lastHeard : Date
+refresh(state : AppState)
+start(with : RuntimeConfig)
+stop()
+handleRecognition(update : RecognitionUpdate)
-beginCapture(command : String)
-monitorCapture(config : RuntimeConfig)
-finalizeCapture(config : RuntimeConfig)
}
class RuntimeConfig {
+triggers : [String]
+micID : String?
+localeID : String?
+triggerChime : VoiceWakeChime
+sendChime : VoiceWakeChime
}
class RecognitionUpdate {
+transcript : String?
+segments : [WakeWordSegment]
+isFinal : Bool
+error : Error?
+generation : Int
}
VoiceWakeRuntime --> RuntimeConfig : "使用"
VoiceWakeRuntime --> RecognitionUpdate : "处理"

音频处理流程

音频处理采用流水线模式:

sequenceDiagram
participant Mic as 麦克风
participant Engine as AVAudioEngine
participant Tap as 音频采样器
participant Recognizer as 语音识别器
participant Gate as 唤醒词门控
participant UI as 用户界面
Mic->>Engine : 音频数据
Engine->>Tap : 缓冲区采样
Tap->>Recognizer : 语音特征
Recognizer->>Gate : 识别结果
Gate->>UI : 触发事件
UI->>UI : 更新状态显示

唤醒词匹配算法

WakeWordGate 实现了复杂的匹配逻辑:

flowchart TD
A[输入语音片段] --> B[文本规范化]
B --> C[唤醒词令牌化]
C --> D[语音段分析]
D --> E{匹配检查}
E --> |找到匹配| F[验证后触发间隔]
E --> |无匹配| G[继续监听]
F --> |间隔不足| H[等待更多语音]
F --> |间隔充足| I[触发唤醒]
H --> D
G --> D
I --> J[开始录音会话]

匹配算法细节

算法的关键参数包括:

  • 最小后触发间隔:默认 0.45 秒,防止误触发
  • 最小命令长度:默认 1 个词,避免短促声音触发
  • 文本规范化:忽略大小写、重音符号和标点符号
  • 时间窗口:基于语音段的时间戳进行精确匹配

音频质量优化

系统实现了多层次的音频质量优化:

graph LR
subgraph "噪声过滤"
A[自适应噪声门限] --> B[RMS 声音级别检测]
B --> C[动态阈值调整]
end
subgraph "音频增强"
D[音频缓冲] --> E[采样率转换]
E --> F[通道格式适配]
end
subgraph "质量监控"
G[实时电平监测] --> H[性能指标记录]
H --> I[自动调优]
end
A --> D
D --> G

音频参数配置

关键的音频参数包括:

  • 最小语音 RMS:1e-3,用于检测语音活动
  • 噪声提升因子:6.0,提高语音检测的灵敏度
  • 缓冲区大小:2048 字节,平衡延迟和性能
  • 采样率:由系统自动选择,确保最佳质量

用户界面集成

语音唤醒功能与用户界面的集成提供了直观的操作体验:

stateDiagram-v2
[*] --> 空闲
空闲 --> 监听中 : 启动语音唤醒
监听中 --> 检测到 : 唤醒词识别
检测到 --> 录音中 : 开始录音
录音中 --> 发送中 : 静音检测
发送中 --> 空闲 : 发送完成
发送中 --> 录音中 : 继续录音
录音中 --> 空闲 : 取消录音
监听中 --> 推话语模式 : 按住右 Option
推话语模式 --> 录音中 : 开始录音
录音中 --> 空闲 : 释放按键

依赖关系分析

语音唤醒功能的依赖关系展现了清晰的分层架构:

graph TB
subgraph "外部依赖"
A[Apple Speech.framework] --> B[语音识别]
C[AVFoundation] --> D[音频处理]
E[Foundation] --> F[系统服务]
end
subgraph "内部模块"
G[VoiceWakeRuntime] --> H[SwabbleKit]
G --> I[VoiceWakeForwarder]
G --> J[VoiceWakeOverlayController]
H --> K[WakeWordGate]
I --> L[GatewayConnection]
J --> M[VoiceSessionCoordinator]
end
subgraph "配置管理"
N[VoiceWakeSettings] --> O[全局配置]
O --> P[本地存储]
P --> Q[voicewake.json]
end
G --> N
H --> N
I --> N

数据流分析

语音唤醒的数据流遵循严格的处理顺序:

sequenceDiagram
participant User as 用户
participant Runtime as 语音唤醒运行时
participant Gate as 唤醒词门控
participant Forwarder as 消息转发器
participant Gateway as 网关服务
User->>Runtime : 语音输入
Runtime->>Gate : 识别结果
Gate->>Gate : 唤醒词匹配
Gate->>Runtime : 匹配成功
Runtime->>Forwarder : 转发请求
Forwarder->>Gateway : RPC 调用
Gateway-->>Forwarder : 执行结果
Forwarder-->>Runtime : 处理完成
Runtime-->>User : 反馈响应

性能考虑

语音唤醒功能在性能方面采用了多项优化策略:

内存管理优化

  • 延迟初始化:AVAudioEngine 仅在需要时创建,避免应用启动时占用音频资源
  • 自动资源回收:空闲时自动释放音频引擎和相关资源
  • 内存池管理:使用固定大小的缓冲区减少内存分配开销

处理效率优化

  • 异步处理:所有音频处理采用异步模式,避免阻塞主线程
  • 批处理优化:音频缓冲区批量处理,减少回调频率
  • 智能重启:失败时自动重启识别器,确保稳定性

系统资源优化

  • 蓝牙耳机保护:避免在 Voice Wake 关闭时切换到低质量模式
  • CPU 使用率控制:根据音频活动动态调整处理强度
  • 电池优化:在移动设备上自动降低处理频率

WebChat 聊天界面

WebChat 聊天界面主要由两个部分组成:

graph TB
subgraph "macOS 应用层"
A[WebChatSwiftUI.swift] --> B[WebChatManager.swift]
B --> C[WebChatSwiftUIWindowController]
C --> D[OpenClawChatView]
end
subgraph "共享 UI 组件层"
E[ChatView.swift] --> F[ChatViewModel.swift]
F --> G[ChatTransport.swift]
E --> H[ChatMessageViews.swift]
E --> I[ChatTheme.swift]
end
subgraph "网关通信层"
J[GatewayConnection] --> K[WebSocket 连接]
K --> L[chat.history]
K --> M[chat.send]
K --> N[chat.abort]
end
D --> E
C --> D
F --> G
G --> J

核心组件

macOS 窗口控制器

WebChatSwiftUIWindowController 是 macOS 平台的核心控制器,负责管理聊天界面的显示和生命周期:

classDiagram
class WebChatSwiftUIWindowController {
-presentation : WebChatPresentation
-sessionKey : String
-hosting : NSHostingController
-contentController : NSViewController
-window : NSWindow?
-dismissMonitor : Any?
+onClosed : () -> Void
+onVisibilityChanged : (Bool) -> Void
+show()
+presentAnchored(anchorProvider)
+close()
+isVisible : Bool
}
class WebChatPresentation {
<<enumeration>>
window
panel(anchorProvider)
+isPanel : Bool
}
class MacGatewayChatTransport {
+requestHistory(sessionKey)
+sendMessage(sessionKey, message, thinking, idempotencyKey, attachments)
+abortRun(sessionKey, runId)
+listSessions(limit)
+requestHealth(timeoutMs)
+events()
+mapPushToTransportEvent(push)
}
WebChatSwiftUIWindowController --> WebChatPresentation
WebChatSwiftUIWindowController --> MacGatewayChatTransport

聊天视图模型

ChatViewModel 是整个聊天界面的状态管理中心:

classDiagram
class OpenClawChatViewModel {
+messages : [OpenClawChatMessage]
+input : String
+thinkingLevel : String
+isLoading : Bool
+isSending : Bool
+isAborting : Bool
+errorText : String?
+attachments : [OpenClawPendingAttachment]
+healthOK : Bool
+pendingRunCount : Int
+sessionKey : String
+sessionId : String?
+streamingAssistantText : String?
+pendingToolCalls : [OpenClawChatPendingToolCall]
+sessions : [OpenClawChatSessionEntry]
-transport : OpenClawChatTransport
-eventTask : Task
-pendingRuns : Set~String~
-pendingToolCallsById : [String : OpenClawChatPendingToolCall]
+load()
+send()
+abort()
+refresh()
+switchSession(to : )
+addAttachments(urls : )
+removeAttachment(id : )
}
class OpenClawChatTransport {
<<protocol>>
+requestHistory(sessionKey)
+sendMessage(sessionKey, message, thinking, idempotencyKey, attachments)
+abortRun(sessionKey, runId)
+listSessions(limit)
+requestHealth(timeoutMs)
+events()
+setActiveSessionKey(sessionKey)
}
OpenClawChatViewModel --> OpenClawChatTransport

架构概览

WebChat 采用分层架构设计,确保了良好的模块分离和可维护性:

graph TB
subgraph "用户界面层"
A[OpenClawChatView] --> B[ChatMessageViews]
A --> C[ChatTheme]
A --> D[ChatComposer]
end
subgraph "业务逻辑层"
E[OpenClawChatViewModel] --> F[ChatViewModel Operations]
F --> G[Message Processing]
F --> H[Session Management]
F --> I[Attachment Handling]
end
subgraph "传输层"
J[MacGatewayChatTransport] --> K[GatewayConnection]
K --> L[WebSocket Protocol]
L --> M[chat.history]
L --> N[chat.send]
L --> O[chat.abort]
L --> P[sessions.list]
end
subgraph "数据层"
Q[Local State] --> R[Message Cache]
Q --> S[Session Cache]
Q --> T[Attachment Cache]
end
A --> E
E --> J
J --> K
K --> L

详细组件分析

消息渲染引擎

消息渲染引擎是 WebChat 的核心组件之一,负责将原始消息数据转换为美观的用户界面:

sequenceDiagram
participant VM as ChatViewModel
participant View as OpenClawChatView
participant Message as ChatMessageBubble
participant Parser as AssistantTextParser
participant Renderer as ChatMarkdownRenderer
VM->>VM : 处理传入消息
VM->>View : 更新消息列表
View->>Message : 创建消息气泡
Message->>Parser : 解析助手文本
Parser->>Renderer : 渲染 Markdown
Renderer->>Message : 返回渲染内容
Message->>View : 显示最终 UI

消息类型处理

系统支持多种消息类型,每种类型都有特定的渲染逻辑:

消息类型 描述 渲染方式
text 文本消息 标准文本渲染
file/attachment 文件附件 附件卡片显示
toolcall/tool_use 工具调用 工具调用卡片
toolresult/tool_result 工具结果 工具结果卡片
thinking 思考内容 斜体文本显示

实时通信机制

WebChat 使用 WebSocket 实现与网关的实时通信:

sequenceDiagram
participant UI as WebChat UI
participant Transport as MacGatewayChatTransport
participant Gateway as GatewayConnection
participant Stream as AsyncStream
UI->>Transport : 初始化传输层
Transport->>Gateway : 建立 WebSocket 连接
Gateway->>Stream : 创建事件流
Stream->>Transport : 推送聊天事件
Transport->>UI : 分发事件到 ViewModel
UI->>UI : 更新界面状态
Note over UI,Gateway : 实时消息推送流程

事件处理流程

系统支持多种事件类型,每种事件都有相应的处理逻辑:

flowchart TD
Start([接收事件]) --> Type{事件类型}
Type --> |health| Health[健康检查事件]
Type --> |chat| Chat[聊天事件]
Type --> |agent| Agent[代理事件]
Type --> |tick| Tick[Tick 事件]
Type --> |seqGap| Gap[序列间隙事件]
Health --> HealthHandler[更新健康状态]
Chat --> ChatHandler[处理聊天消息]
Agent --> AgentHandler[处理工具调用]
Tick --> TickHandler[轮询健康状态]
Gap --> GapHandler[刷新历史记录]
HealthHandler --> End([完成])
ChatHandler --> End
AgentHandler --> End
TickHandler --> End
GapHandler --> End

会话管理

WebChat 支持多会话管理,用户可以在不同会话之间切换:

classDiagram
class WebChatManager {
+windowController : WebChatSwiftUIWindowController?
+panelController : WebChatSwiftUIWindowController?
+cachedPreferredSessionKey : String?
+show(sessionKey)
+togglePanel(sessionKey, anchorProvider)
+closePanel()
+preferredSessionKey()
+resetTunnels()
}
class SessionCache {
+sessions : [OpenClawChatSessionEntry]
+lastUpdated : Date
+cacheDuration : TimeInterval
+getCachedSession(key)
+updateCache(sessions)
}
class SessionValidator {
+validateSessionKey(key)
+normalizeSessionKey(key)
+checkSessionExists(key)
}
WebChatManager --> SessionCache
WebChatManager --> SessionValidator

主题定制系统

WebChat 提供了灵活的主题定制系统,支持深色和浅色模式:

classDiagram
class OpenClawChatTheme {
+surface : Color
+background : View
+card : Color
+subtleCard : AnyShapeStyle
+userBubble : Color
+assistantBubble : Color
+onboardingAssistantBubble : Color
+userText : Color
+assistantText : Color
+composerBackground : AnyShapeStyle
+composerField : AnyShapeStyle
+composerBorder : Color
+divider : Color
}
class ChatBubbleShape {
+cornerRadius : CGFloat
+tail : Tail
+insetAmount : CGFloat
+path(in : CGRect)
}
class ThemeManager {
+currentTheme : OpenClawChatTheme
+applyTheme(theme)
+updateThemeForAppearance(appearance)
+getUserPreference()
}
OpenClawChatTheme --> ChatBubbleShape
ThemeManager --> OpenClawChatTheme

主题变量说明

主题变量 用途 默认值
surface 背景表面颜色 系统窗口背景色
userBubble 用户消息气泡颜色 自定义蓝色调
assistantBubble 助手消息气泡颜色 系统背景色
userText 用户文本颜色 白色
assistantText 助手文本颜色 系统标签色
composerBackground 输入框背景 材质效果
composerField 输入区域样式 材质效果

附件处理系统

WebChat 支持多种类型的附件处理:

flowchart TD
Upload[用户上传附件] --> Validate[验证附件]
Validate --> SizeCheck{大小检查}
SizeCheck --> |超过限制| Error[显示错误]
SizeCheck --> |符合要求| TypeCheck{类型检查}
TypeCheck --> |图片| ImageProcess[图片处理]
TypeCheck --> |其他| OtherProcess[其他类型处理]
ImageProcess --> Preview[生成预览]
OtherProcess --> Store[存储附件]
Preview --> AddToList[添加到附件列表]
Store --> AddToList
AddToList --> Send[发送消息]
Error --> End[结束]
Send --> End

依赖关系分析

WebChat 的依赖关系清晰明确,遵循单一职责原则:

graph TB
subgraph "外部依赖"
A[SwiftUI] --> B[AppKit/UIKit]
C[Foundation] --> D[Observation]
E[OSLog] --> F[UniformTypeIdentifiers]
end
subgraph "内部模块"
G[OpenClawChatUI] --> H[ChatView]
G --> I[ChatViewModel]
G --> J[ChatTransport]
G --> K[ChatTheme]
G --> L[ChatMessageViews]
M[OpenClawKit] --> N[GatewayConnection]
M --> O[AnyCodable]
M --> P[ToolDisplay]
Q[OpenClawProtocol] --> R[GatewayModels]
Q --> S[AnyCodable]
end
subgraph "平台特定"
T[macOS] --> U[NSWindow]
T --> V[NSHostingController]
W[iOS] --> X[UIViewController]
W --> Y[UIHostingController]
end
H --> G
I --> G
J --> M
K --> G
L --> G
G --> M
M --> Q

性能考虑

内存管理

WebChat 采用了多项内存优化策略:

  1. 懒加载消息列表:使用 LazyVStack 减少内存占用
  2. 消息去重算法:避免重复消息占用内存
  3. 附件缓存管理:限制附件大小和数量
  4. 任务取消机制:及时取消不再需要的任务

渲染优化

flowchart TD
Start([消息渲染开始]) --> CheckCache{检查缓存}
CheckCache --> |命中| UseCache[使用缓存内容]
CheckCache --> |未命中| ParseText[解析文本内容]
ParseText --> CheckType{检查消息类型}
CheckType --> |普通文本| RenderText[渲染文本]
CheckType --> |Markdown| ParseMarkdown[解析 Markdown]
CheckType --> |附件| RenderAttachment[渲染附件]
CheckType --> |工具调用| RenderToolCall[渲染工具调用]
ParseMarkdown --> RenderText
RenderAttachment --> OptimizeImage[优化图片]
OptimizeImage --> RenderText
UseCache --> End([渲染完成])
RenderText --> End

网络优化

  1. 事件流管理:使用 AsyncStream 高效处理实时事件
  2. 健康检查轮询:智能轮询策略减少网络开销
  3. 序列间隙检测:自动检测并处理网络中断
  4. 超时处理:合理的超时设置避免资源泄露

万字解析 OpenClaw 源码架构-跨平台应用之Android 应用

作者 毛骗导演
2026年3月14日 08:46

Android 应用层的关键文件组织如下:

  • 应用入口与运行时:NodeApp、NodeRuntime
  • 前台服务:NodeForegroundService
  • 网关会话:GatewaySession(WebSocket 客户端)
  • 平台服务:DeviceNotificationListenerService(通知监听)
  • 设备信息与健康:DeviceHandler(电池、内存等)
  • 安全与偏好:SecurePrefs
  • UI 绑定:MainViewModel(对 NodeRuntime 的调用封装)
graph TB
subgraph "应用层"
A["NodeApp<br/>应用入口"]
B["NodeRuntime<br/>节点运行时"]
C["NodeForegroundService<br/>前台服务"]
D["DeviceNotificationListenerService<br/>通知监听服务"]
end
subgraph "网关层"
E["GatewaySession<br/>WebSocket 会话"]
end
subgraph "设备与系统"
F["DeviceHandler<br/>设备信息/健康"]
G["SecurePrefs<br/>加密偏好"]
end
A --> B
B --> C
B --> E
B --> F
B --> G
D --> B

核心组件

  • NodeApp:应用生命周期持有者,延迟初始化 NodeRuntime
  • NodeRuntime:核心运行时,负责:
    • 网关连接(操作员会话与节点会话)
    • 自动重连与断线恢复
    • 状态流(连接状态、服务器名、麦克风状态等)
    • 事件分发与命令派发(InvokeDispatcher)
    • 通知监听事件转发到节点会话
  • NodeForegroundService:前台服务,负责:
    • 创建并更新通知
    • 订阅 NodeRuntime 状态流,动态刷新通知内容
    • 提供“断开”动作以触发 NodeRuntime 断开连接
  • GatewaySession:WebSocket 客户端,封装连接、请求、事件与自动重连循环
  • DeviceNotificationListenerService:系统通知监听服务,将通知事件转发给 NodeRuntime
  • DeviceHandler:设备健康信息采集(电池、内存、温度等)
  • SecurePrefs:加密存储(SharedPreferences 加密包装),用于实例 ID、显示名、网关凭据等

架构总览

下图展示从前台服务到运行时、再到网关会话的整体交互流程。

sequenceDiagram
participant Sys as "系统"
participant Svc as "NodeForegroundService"
participant RT as "NodeRuntime"
participant GS as "GatewaySession"
participant GW as "网关"
Sys->>Svc : "启动前台服务"
Svc->>RT : "订阅状态流状态/服务器/连接/Mic"
RT-->>Svc : "状态变化连接/断开/麦克风状态"
Svc->>Svc : "构建/更新通知"
Note over Svc : "点击“断开”触发停止动作"
Svc->>RT : "disconnect()"
RT->>GS : "关闭会话/取消重连"
GS-->>RT : "onDisconnected 回调"
RT-->>Svc : "状态流更新"
Svc->>Svc : "更新通知为断开状态"

详细组件分析

NodeForegroundService 前台服务

  • 职责
    • 在应用启动后立即以前台服务方式运行,避免被系统回收
    • 订阅 NodeRuntime 的多源状态流,动态更新通知标题与文本
    • 提供“断开”动作,通过广播触发 NodeRuntime 断开连接并停止自身
  • 关键点
    • 使用通知通道(IMPORTANCE_LOW)与 FOREGROUND_SERVICE_TYPE_DATA_SYNC
    • 通知设置为 ongoing 且仅提示一次,确保用户可随时查看
    • 首次启动时调用 startForegroundWithTypes,后续使用 notify 更新
  • 生命周期
    • onCreate:创建通知通道、初始通知、订阅状态流
    • onStartCommand:处理 ACTION_STOP 动作,触发断开并 stopSelf
    • onDestroy:取消协程作业,释放资源
flowchart TD
Start(["服务启动"]) --> Ensure["创建通知通道"]
Ensure --> InitNotify["构建初始通知并启动前台"]
InitNotify --> Subscribe["订阅 NodeRuntime 状态流"]
Subscribe --> OnChange{"状态变化?"}
OnChange --> |是| Build["构建新通知"]
Build --> Update["notify 更新"]
OnChange --> |否| Wait["等待下次变化"]
Update --> Wait
Wait --> OnChange
StopAction["收到 ACTION_STOP"] --> Disconnect["调用 NodeRuntime.disconnect()"]
Disconnect --> StopSelf["stopSelf()"]

NodeRuntime 节点运行时与生命周期控制

  • 职责
    • 管理两个 GatewaySession:操作员会话与节点会话
    • 维护连接状态、服务器名、远端地址、主会话键等状态流
    • 自动发现与自动连接(受信任网关 TLS 指纹约束)
    • 将通知监听事件转发至节点会话
    • 管理麦克风、TTS、Canvas 等子系统
  • 连接与重连
    • runLoop 循环尝试连接,失败指数退避,保持持续重连
    • onConnected/onDisconnected 回调驱动状态流更新
  • 状态流
    • isConnected、statusText、serverName、remoteAddress、micEnabled、micIsListening 等
    • 通过 combine 组合多个流,驱动前台通知实时更新
  • 生命周期控制
    • setForeground:切换前后台,必要时停止语音会话
    • disconnect:清空目标端点并断开会话
classDiagram
class NodeRuntime {
+connect(endpoint)
+disconnect()
+refreshGatewayConnection()
+setForeground(Boolean)
+statusText : StateFlow
+isConnected : StateFlow
+serverName : StateFlow
+remoteAddress : StateFlow
+micEnabled : StateFlow
+micIsListening : StateFlow
}
class GatewaySession {
+connect(endpoint, token, password, options, tls)
+disconnect()
+reconnect()
+request(method, paramsJson)
+sendNodeEvent(event, payloadJson)
}
NodeRuntime --> GatewaySession : "管理两个会话"

网关会话(GatewaySession)与自动重连

  • 运行循环
    • runLoop:根据 desired 目标连接;失败则延迟重试,指数退避上限 8 秒
    • connectOnce:建立单次连接,等待关闭或异常
  • 请求与事件
    • request:发送 RPC 请求并等待响应,超时抛出异常
    • handleEvent:处理事件帧,识别 node.invoke.request 并派发到 onInvoke
  • TLS 与指纹
    • 支持 TLS 参数与指纹回调,首次连接时探测并保存指纹,后续自动信任
flowchart TD
Loop["runLoop 开始"] --> Target{"存在目标连接?"}
Target --> |否| Close["关闭当前连接/等待"] --> Delay["delay(250ms)"] --> Loop
Target --> |是| Connect["connectOnce 连接"]
Connect --> Ok{"连接成功?"}
Ok --> |是| Run["保持连接/等待关闭"]
Ok --> |否| Retry["增加尝试次数"] --> Backoff["指数退避(<=8s)"] --> Loop

通知监听服务与状态同步

  • DeviceNotificationListenerService
    • 实现 onNotificationPosted,解析通知为内部条目并写入存储
    • 过滤自身包名的通知,向 NodeEventSink 发送变更事件
  • 事件同步
    • NodeRuntime 初始化时设置事件 Sink,将事件转发到节点会话(node.event)
    • 前台服务订阅 NodeRuntime 状态流,实现 UI/通知与运行时状态的双向同步
sequenceDiagram
participant OS as "系统通知栏"
participant NLS as "DeviceNotificationListenerService"
participant Sink as "NodeEventSink"
participant NR as "NodeRuntime"
participant NS as "Node Session"
OS->>NLS : "通知发布"
NLS->>NLS : "解析为条目/写入存储"
NLS->>Sink : "emitNotificationsChanged(...)"
Sink->>NR : "转发事件"
NR->>NS : "sendNodeEvent(event, payload)"

设备健康与内存管理

  • DeviceHandler
    • 读取电池快照(状态、充电、电量分数、温度)
    • 读取内存快照(总内存、可用内存、是否低内存、压力等级)
    • 输出 JSON 负载供上层使用
  • 内存与电池指标可用于:
    • UI 健康指示
    • 自适应降级(如降低麦克风采样率、暂停非关键任务)

安全与配置

  • 权限与前台服务类型
    • AndroidManifest 声明 FOREGROUND_SERVICE、FOREGROUND_SERVICE_DATA_SYNC、INTERNET 等
    • NodeForegroundService 使用 FOREGROUND_SERVICE_TYPE_DATA_SYNC
  • 加密存储
    • SecurePrefs 使用 EncryptedSharedPreferences 存储网关令牌、密码、TLS 指纹等
    • 实例 ID 与显示名自动生成/迁移,保证唯一性与隐私
  • TLS 指纹校验
    • 首次连接探测指纹,用户确认后持久化,后续自动信任

依赖关系分析

  • 组件耦合
    • NodeForegroundService 强依赖 NodeRuntime 的状态流,弱依赖 NodeApp(获取 runtime)
    • NodeRuntime 依赖 GatewaySession(两个会话)、DeviceNotificationListenerService(事件源)、DeviceHandler(健康数据)、SecurePrefs(配置与凭据)
    • GatewaySession 依赖 OkHttp WebSocket、DeviceIdentityStore、DeviceAuthStore
  • 外部依赖
    • Android 系统服务:通知、前台服务、通知监听服务
    • OkHttp:WebSocket 客户端
  • 可能的循环依赖
    • 当前结构无直接循环依赖;事件通过回调与状态流解耦
graph LR
Svc["NodeForegroundService"] --> RT["NodeRuntime"]
RT --> GS["GatewaySession"]
RT --> DH["DeviceHandler"]
RT --> NLS["DeviceNotificationListenerService"]
RT --> SP["SecurePrefs"]
GS --> OkHttp["OkHttp WebSocket"]

性能与电池优化

  • 通知即时行为
    • 使用 FOREGROUND_SERVICE_IMMEDIATE,确保通知立即可见,减少用户感知延迟
  • 协程与背压
    • 使用 SupervisorJob + IO 主线程,避免主线程阻塞
    • 状态流合并使用 distinctUntilChanged,减少无效 UI 刷新
  • 自动重连与退避
    • 指数退避上限 8 秒,降低网络波动对系统的影响
  • 电池与内存
    • DeviceHandler 提供电池/内存快照,便于在低电量/低内存时降级处理
  • 建议
    • 在低电量/低内存场景下暂停非关键任务(如 Canvas 重载)
    • 控制通知频率,避免频繁更新导致 CPU/电量消耗
    • 对长耗时任务拆分为小步,配合 WorkManager 或前台服务

界面组件

Android UI 采用 Jetpack Compose 架构,以“主题层 → 布局层 → 屏幕层 → 功能组件层”的分层组织方式:

  • 主题层:统一颜色、字体与动态色方案
  • 布局层:顶部状态栏、底部导航、Scaffold 容器
  • 屏幕层:引导流程、标签页容器、各功能页(聊天、语音、屏幕、设置)
  • 功能组件层:聊天输入区、消息气泡、工具调用提示、错误提示等
graph TB
A["MainActivity<br/>设置主题与根内容"] --> B["OpenClawTheme<br/>Material3 动态色"]
B --> C["RootScreen<br/>引导/标签页入口"]
C --> D["PostOnboardingTabs<br/>Scaffold + TabBar"]
D --> E["ConnectTabScreen"]
D --> F["ChatSheetContent<br/>消息列表 + 输入区"]
D --> G["VoiceTabScreen"]
D --> H["ScreenTabScreen"]
D --> I["SettingsSheet<br/>权限与设备配置"]

核心组件

  • 主题系统:基于 Material3 动态色,提供覆盖容器色与图标色的辅助函数,确保深浅模式一致体验
  • 移动 UI 令牌:集中定义颜色、字体族与排版样式,统一视觉语言
  • 根屏幕:根据引导完成状态切换到标签页容器
  • 标签页容器:顶部状态栏 + 底部导航 + 内容区域,支持 IME 折叠与 Tab 切换
  • 设置页面:集中管理权限、位置模式、睡眠策略、通知与数据访问等
  • 聊天界面:会话选择、消息列表、输入区(文本+图片附件)、工具调用提示、实时流式助手

架构总览

Compose 驱动的单 Activity 多屏幕架构,通过 ViewModel 暴露状态流,屏幕层订阅并渲染 UI;功能组件通过参数回调与 ViewModel 交互,形成清晰的数据流向。

sequenceDiagram
participant A as "MainActivity"
participant B as "OpenClawTheme"
participant C as "RootScreen"
participant D as "PostOnboardingTabs"
participant E as "MainViewModel"
participant F as "ChatSheetContent"
participant G as "ChatComposer"
A->>B : "设置主题"
B->>C : "包裹根内容"
C->>D : "根据引导状态切换"
D->>E : "订阅状态流"
D->>F : "加载聊天/语音/屏幕/设置"
F->>E : "读取消息/会话/健康状态"
F->>G : "传递发送/刷新/中止回调"
G->>E : "触发发送/中止/切换思考级别"

详细组件分析

主题系统与颜色/字体令牌

  • 动态色方案:根据系统深浅模式选择动态亮/暗配色,作为 MaterialTheme 的 colorScheme
  • 覆盖容器色与图标色:提供 overlayContainerColor 与 overlayIconColor,用于浮层与覆盖 UI 的一致性
  • 颜色令牌:集中定义背景、表面、边框、文本、强调色、成功/警告/危险等语义色
  • 字体令牌:定义字体族与多级排版样式(标题、正文、说明、脚注等),统一在组件中引用
classDiagram
class OpenClawTheme {
+OpenClawTheme(content)
+overlayContainerColor()
+overlayIconColor()
}
class MobileUiTokens {
+mobileBackgroundGradient
+mobileSurface
+mobileText*
+mobileAccent*
+mobileCode*
+mobileFontFamily
+mobileTitle1/mobileTitle2
+mobileHeadline/mobileBody
+mobileCallout/mobileCaption1/mobileCaption2
}
OpenClawTheme --> MobileUiTokens : "使用颜色/字体令牌"

标签页容器与状态指示器

  • 顶部状态栏:根据连接状态映射为不同视觉状态(已连接、连接中、警告、错误、离线),并以色块与点标示
  • 底部导航:Tab 切换时高亮当前项,结合 IME 显示隐藏规则优化输入体验
  • 内容区域:按需渲染各功能页,如屏幕页支持“恢复仪表盘”提示
flowchart TD
A["状态文本"] --> B{"解析状态"}
B --> |已连接| C["Connected<br/>绿色系"]
B --> |连接中/重连| D["Connecting<br/>蓝色系"]
B --> |配对/授权| E["Warning<br/>橙色系"]
B --> |错误/失败| F["Error<br/>红色系"]
B --> |默认| G["Offline<br/>灰阶"]

聊天界面

  • 会话选择:横向滚动展示历史会话,当前会话高亮
  • 错误提示:错误文本出现时以警示色块显示
  • 消息列表:支持文本、Markdown、图片、工具调用、流式助手等多类型渲染
  • 输入区:支持文本输入、图片附件、思考级别选择、刷新/中止、发送按钮与禁用态
sequenceDiagram
participant U as "用户"
participant CS as "ChatSheetContent"
participant VM as "MainViewModel"
participant CC as "ChatComposer"
participant MV as "ChatMessageViews"
U->>CS : "打开聊天页"
CS->>VM : "订阅消息/会话/健康状态"
CS->>CC : "传入发送/刷新/中止回调"
U->>CC : "输入文本/选择图片/选择思考级别"
CC->>VM : "sendChat(message, thinking, attachments)"
VM-->>CS : "更新消息/健康状态"
CS->>MV : "渲染消息列表"

设置页面

  • 设备信息:实例 ID、设备型号、版本号
  • 权限管理:麦克风、相机、短信、通知、照片、联系人、日历、运动、位置(含精确位置)
  • 位置模式:Off/WhileUsing/Precise Location 三态控制
  • 屏幕常亮:防止休眠开关
  • 交互流程:权限请求通过 ActivityResultLauncher 触发,状态变更通过 ViewModel 同步
flowchart TD
A["进入设置页"] --> B["读取权限状态"]
B --> C{"是否已授予?"}
C --> |是| D["显示“管理”按钮"]
C --> |否| E["显示“授权”按钮"]
D --> F["打开系统设置或执行操作"]
E --> G["启动权限请求"]
G --> H["回调后更新状态"]

数据流与状态管理

  • ViewModel 暴露 StateFlow,屏幕与组件通过 collectAsState 订阅
  • 聊天:消息列表、错误、健康状态、思考级别、流式助手文本、待执行工具调用、会话列表
  • 连接:网关发现状态、连接状态、远端地址、服务器名
  • 语音/屏幕/节点:相机、Canvas 控制、前台服务等
classDiagram
class MainViewModel {
+canvasCurrentUrl/statusText/isConnected
+chatMessages/chatError/chatHealthOk
+chatThinkingLevel/chatStreamingAssistantText
+chatSessions/pendingRunCount
+locationMode/locationPreciseEnabled
+preventSleep/cameraEnabled
+displayName/instanceId
+set*()/connect()/disconnect()
}
class ChatSheetContent
class ChatComposer
class SettingsSheet
ChatSheetContent --> MainViewModel : "收集状态/触发操作"
ChatComposer --> MainViewModel : "发送/中止/切换思考级别"
SettingsSheet --> MainViewModel : "权限/位置/睡眠策略"

依赖关系分析

  • 组件耦合:屏幕层仅依赖 ViewModel 的 StateFlow,功能组件通过回调与 ViewModel 解耦
  • 主题与样式:所有 UI 组件统一引用 MobileUiTokens 中的颜色与排版,避免硬编码
  • 权限与系统服务:设置页通过 ActivityResultLauncher 与系统权限对话交互,避免直接持有上下文引用
graph LR
MV["MainViewModel"] --> RC["RootScreen"]
RC --> POT["PostOnboardingTabs"]
POT --> CNT["ChatSheetContent"]
POT --> SET["SettingsSheet"]
CNT --> CMP["ChatComposer"]
CNT --> MSG["ChatMessageViews"]
CMP --> THEME["OpenClawTheme"]
MSG --> THEME
SET --> THEME

性能考量

  • 布局折叠:底部导航在 IME 弹出时自动隐藏,减少重绘范围
  • 滚动优化:消息列表与会话选择使用水平滚动与懒加载容器,限制一次性渲染量
  • 图片处理:图片附件在 IO 线程解码与 Base64 编码,完成后在主线程更新 UI
  • 状态订阅:仅在必要作用域内订阅 StateFlow,避免过度重组
  • 主题与样式:集中定义样式令牌,减少重复计算与资源分配

权限与安全

Android 应用位于 apps/android/app,核心安全与权限相关文件分布如下:

  • 权限请求:PermissionRequester.kt
  • 安全偏好:SecurePrefs.kt
  • 清单与服务:AndroidManifest.xml
  • 构建与依赖:build.gradle.kts
  • 网络与备份配置:network_security_config.xml、backup_rules.xml
  • TLS 固定:GatewayTls.kt
  • 设备权限状态上报:DeviceHandler.kt
  • 设置界面权限状态展示:SettingsSheet.kt
  • 安全偏好测试:SecurePrefsTest.kt
graph TB
subgraph "Android 应用"
A["PermissionRequester<br/>权限请求器"]
B["SecurePrefs<br/>安全偏好设置"]
C["AndroidManifest<br/>权限与服务声明"]
D["build.gradle.kts<br/>构建与依赖"]
E["network_security_config.xml<br/>网络安全策略"]
F["backup_rules.xml<br/>备份规则"]
G["GatewayTls<br/>TLS 固定"]
H["DeviceHandler<br/>设备权限状态上报"]
I["SettingsSheet<br/>设置界面权限状态"]
J["SecurePrefsTest<br/>安全偏好测试"]
end
A --> C
B --> C
G --> C
H --> C
I --> C
D --> C
E --> C
F --> C
J --> B

核心组件

  • 权限请求器(PermissionRequester):封装多权限一次性请求、理由说明对话框、超时控制与设置引导。
  • 安全偏好(SecurePrefs):基于 EncryptedSharedPreferences 的敏感数据加密存储,同时维护明文偏好用于非敏感配置;支持实例 ID、网关凭据、唤醒词等。
  • 清单与服务(AndroidManifest):声明网络、定位、相机、麦克风、通知、短信、媒体访问、日历联系人等权限;注册前台服务、通知监听服务、FileProvider。
  • 网络安全策略(network_security_config.xml):允许本地与特定域名的清晰文本流量,适配受信尾随网络场景。
  • 备份规则(backup_rules.xml):启用全量文件备份。
  • TLS 固定(GatewayTls):自定义 TrustManager 实现证书指纹校验,支持首次信任(TOFU)与持久化存储。
  • 设备权限状态上报(DeviceHandler):汇总设备权限状态并以 JSON 形式上报。
  • 设置界面权限状态展示(SettingsSheet):在设置页动态检查并显示各类权限状态。

架构总览

下图展示了从“权限请求”到“安全存储”再到“网络通信”的整体链路,以及与清单和服务的关系。

sequenceDiagram
participant UI as "设置界面/调用方"
participant PR as "PermissionRequester"
participant AM as "Android 系统权限框架"
participant SP as "SecurePrefs"
participant TLS as "GatewayTls"
participant NET as "远端网关"
UI->>PR : 请求若干运行时权限
PR->>AM : 发起多权限请求
AM-->>PR : 返回授权结果
PR-->>UI : 合并当前状态并提示设置入口
UI->>SP : 写入/读取敏感配置如网关令牌
SP-->>UI : 返回解密后的值
UI->>TLS : 基于参数构建 TLS 配置
TLS-->>UI : 返回校验证书的 SSL Socket 工厂
UI->>NET : 使用已校验的 TLS 连接进行通信

详细组件分析

权限请求器(PermissionRequester)

  • 功能要点
    • 批量检测缺失权限,必要时弹出理由对话框,尊重用户选择。
    • 使用 ActivityResultLauncher 触发系统授权对话框,支持超时控制。
    • 合并当前授权状态与回调结果,对被拒绝且无理由的权限引导至系统设置。
  • 关键行为
    • 检测逻辑:仅对未授予的权限发起请求。
    • 超时与并发:通过互斥锁与超时机制避免阻塞与竞态。
    • 结果合并:若某权限在回调前已被授予,视为已授权。
  • 用户体验
    • 对相机、麦克风、短信等权限提供明确标签提示。
    • 对不可恢复权限(无理由)引导至应用详情页设置。
flowchart TD
Start(["开始"]) --> Detect["检测缺失权限"]
Detect --> AnyMissing{"存在缺失权限?"}
AnyMissing --> |否| ReturnAllTrue["返回全部已授权"]
AnyMissing --> |是| NeedRationale{"是否需要理由说明?"}
NeedRationale --> |是| ShowRationale["显示理由对话框"]
ShowRationale --> UserChoice{"用户同意?"}
UserChoice --> |否| ReturnCurrent["返回当前授权状态"]
UserChoice --> |是| Launch["启动系统权限请求"]
NeedRationale --> |否| Launch
Launch --> Await["等待授权结果或超时"]
Await --> Merge["合并当前状态与回调结果"]
Merge --> Denied{"是否存在被拒绝且无理由的权限?"}
Denied --> |是| OpenSettings["引导打开应用设置"]
Denied --> |否| Done["完成"]
OpenSettings --> Done
ReturnAllTrue --> End(["结束"])
ReturnCurrent --> End
Done --> End

安全偏好设置(SecurePrefs)

  • 数据隔离
    • 明文偏好:存放非敏感配置(如实例 ID、显示名、位置模式、唤醒词等)。
    • 加密偏好:存放敏感信息(如网关令牌、密码),使用 EncryptedSharedPreferences 与 AES256-GCM。
  • 主密钥与加密方案
    • 使用 MasterKey.AES256_GCM 作为主密钥,Key/Value 分别采用 AES256_SIV 与 AES256_GCM。
  • 生命周期与状态流
    • 大部分字段以 StateFlow 暴露,便于 UI 订阅。
  • 迁移与兼容
    • 对历史键值进行迁移(例如位置模式的“always”迁移到新枚举值)。
  • 测试覆盖
    • 单元测试验证迁移逻辑正确性。
classDiagram
class SecurePrefs {
-appContext : Context
-json : Json
-plainPrefs : SharedPreferences
-masterKey : MasterKey
-securePrefs : SharedPreferences
-_instanceId : StateFlow~String~
-_displayName : StateFlow~String~
-_locationMode : StateFlow~LocationMode~
-_gatewayToken : StateFlow~String~
+setDisplayName(value)
+setLocationMode(mode)
+setGatewayToken(token)
+saveGatewayToken(token)
+loadGatewayToken() String?
+saveGatewayPassword(password)
+loadGatewayPassword() String?
+getString(key) String?
+putString(key,value)
+remove(key)
}

Android 清单与权限配置(AndroidManifest)

  • 权限声明
    • 网络与前台服务:INTERNET、ACCESS_NETWORK_STATE、FOREGROUND_SERVICE、FOREGROUND_SERVICE_DATA_SYNC。
    • 通知与 Wi‑Fi:POST_NOTIFICATIONS、NEARBLY_WIFI_DEVICES(含 flags)、ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION。
    • 媒体与存储:CAMERA、RECORD_AUDIO、SEND_SMS、READ_MEDIA_IMAGES、READ_MEDIA_VISUAL_USER_SELECTED、READ_EXTERNAL_STORAGE(maxSdk=32)。
    • 联系人与日历:READ_CONTACTS、WRITE_CONTACTS、READ_CALENDAR、WRITE_CALENDAR。
    • 行为识别:ACTIVITY_RECOGNITION。
  • 特性声明
    • 摄像头与电话硬件为可选(required=false)。
  • 服务与 Provider
    • 前台服务、通知监听服务、FileProvider(用于分享文件)。
graph LR
M["AndroidManifest"] --> P1["网络/服务权限"]
M --> P2["媒体/存储权限"]
M --> P3["位置/通知权限"]
M --> P4["联系人/日历权限"]
M --> S1["前台服务"]
M --> S2["通知监听服务"]
M --> PDR["FileProvider"]

网络安全策略与备份规则

  • 网络安全配置
    • 允许基础清晰文本流量,针对 openclaw.local 与 ts.net 子域开放 HTTP。
    • 适用于受信尾随网络环境,降低本地开发与内网场景的 TLS 成本。
  • 备份规则
    • 启用全量文件备份,注意敏感数据应仅存于加密偏好中。

TLS 固定与安全通信

  • 目标
    • 在客户端侧对远端网关证书进行指纹校验,防止中间人攻击。
  • 实现
    • 自定义 TrustManager:当提供期望指纹时严格比对;否则回退到默认信任策略。
    • 支持首次信任(TOFU):若允许且首次成功握手,则将指纹持久化。
    • 提供 HostnameVerifier 与 SSLSocketFactory,便于 OkHttp 或原生 HTTPS 使用。
  • 使用建议
    • 对外网与非受信网络强制启用 TLS 与指纹校验;对本地环回地址可放宽策略。
sequenceDiagram
participant APP as "应用"
participant TLS as "GatewayTls"
participant CHAIN as "证书链"
participant STORE as "指纹存储"
APP->>TLS : 传入 GatewayTlsParams
TLS->>CHAIN : 获取首张证书并计算 SHA-256
alt 提供期望指纹
TLS->>TLS : 比对指纹一致?
TLS-->>APP : 一致则允许,否则取消
else 允许 TOFU
TLS->>STORE : 持久化指纹
TLS-->>APP : 允许连接
else 默认策略
TLS->>TLS : 使用系统信任策略校验
TLS-->>APP : 结果由系统决定
end

设备权限状态上报与设置页展示

  • 设备权限状态上报
    • DeviceHandler 汇总相机、麦克风、位置、照片、联系人、日历、运动、通知等权限状态,生成 JSON。
  • 设置页展示
    • SettingsSheet 在 onResume 时检查各权限状态,更新 UI 展示。

依赖关系分析

  • 构建与依赖
    • Compose、OkHttp、安全加密库(androidx.security:security-crypto)、相机库等。
    • 测试框架(Robolectric、Kotest、MockWebServer)。
  • 运行时依赖
    • 权限请求依赖 ActivityResultContracts 与 ActivityCompat。
    • 安全存储依赖 EncryptedSharedPreferences 与 MasterKey。
    • TLS 固定依赖 javax.net.ssl 与系统信任管理器。
graph TB
BR["build.gradle.kts"] --> ACT["AndroidX Activity"]
BR --> SEC["AndroidX Security Crypto"]
BR --> OK["OkHttp"]
BR --> CAM["CameraX"]
PR["PermissionRequester"] --> ACT
SP["SecurePrefs"] --> SEC
TLS["GatewayTls"] --> OK

性能考量

  • 权限请求
    • 使用互斥锁避免并发重复请求;超时控制防止 UI 卡死。
    • 合并当前状态与回调结果,减少后续判断成本。
  • 安全存储
    • EncryptedSharedPreferences 会带来一定开销,建议批量写入与合理缓存。
    • 对频繁读取的键值可引入内存缓存(当前实现以 StateFlow 为主)。
  • TLS 固定
    • 证书指纹计算与持久化需在后台线程执行,避免阻塞主线程。
    • 首次握手失败重试与超时控制,提升稳定性。

设备控制

Android 节点应用位于 apps/android/app,核心控制逻辑集中在 node 包中;网关侧策略与测试位于 src/gateway。

graph TB
subgraph "Android 节点"
ID["InvokeDispatcher<br/>命令分发器"]
DH["DeviceHandler<br/>设备信息/权限/健康"]
CH["CameraHandler<br/>相机列表/拍照/录像"]
CNH["ContactsHandler<br/>联系人搜索/新增"]
NH["NotificationsHandler<br/>通知快照/动作"]
SH["SmsHandler<br/>短信发送"]
LH["LocationHandler<br/>位置获取"]
AH["A2UIHandler<br/>A2UI 就绪/消息应用"]
end
subgraph "网关"
POL["node-command-policy.ts<br/>命令白名单/平台默认值"]
TEST["android-node.capabilities.live.test.ts<br/>能力验证测试"]
DISC["GatewayDiscovery.kt<br/>mDNS/NSD 发现"]
PAIR["message-handler.ts<br/>配对握手/拒绝"]
PNOTI["device-pair/notify.ts<br/>配对提醒轮询"]
end
ID --> DH
ID --> CH
ID --> CNH
ID --> NH
ID --> SH
ID --> LH
ID --> AH
POL --> ID
TEST --> ID
DISC --> ID
PAIR --> ID
PNOTI --> ID

核心组件

  • 命令分发器(InvokeDispatcher)
    • 统一入口,根据命令名路由到对应处理器,并进行前台限制、能力可用性检查与错误包装
    • 支持 Canvas/A2UI、相机、位置、设备、通知、系统、相册、联系人、日历、运动、短信等命令族
  • 各子处理器
    • DeviceHandler:设备状态、信息、权限、健康度
    • CameraHandler:相机设备枚举、拍照、录视频(含大小限制与错误处理)
    • ContactsHandler:联系人搜索、新增(含权限校验与批量插入)
    • NotificationsHandler:通知快照、动作执行(打开/忽略/回复)
    • SmsHandler:短信发送(错误码映射)
    • LocationHandler:位置获取(权限、精度、超时)
    • A2UIHandler:A2UI 主机解析、就绪检测、消息应用
  • 网关侧策略
    • node-command-policy.ts:按平台定义默认命令集、危险命令白名单、节点声明校验
  • 文档与测试
    • 平台文档:Android 节点连接、命令面与注意事项
    • 能力测试:覆盖 Canvas、相机、位置、设备、通知、调试等命令

架构总览

Android 节点通过 mDNS/NSD 发现网关,建立 WebSocket 连接并完成配对。命令由网关侧策略决定是否允许,节点侧通过 InvokeDispatcher 分发至各 Handler 执行,结果回传给网关。

sequenceDiagram
participant Node as "Android 节点"
participant Disc as "GatewayDiscovery<br/>mDNS/NSD"
participant GW as "Gateway"
participant Policy as "node-command-policy.ts"
participant Disp as "InvokeDispatcher"
participant H as "各 Handler"
Node->>Disc : 发现 _openclaw-gw._tcp
Disc-->>Node : 返回网关地址/端口
Node->>GW : 建立 WebSocket 连接
GW-->>Node : 握手/配对请求
alt 未配对且非静默
GW-->>Node : 拒绝连接并提示配对
else 已配对或静默
GW-->>Node : 允许连接
end
Node->>GW : node.invoke(command, params)
GW->>Policy : 校验命令是否允许
Policy-->>GW : 允许/拒绝
GW->>Disp : 调用分发器
Disp->>H : 路由到具体处理器
H-->>Disp : 执行结果/错误
Disp-->>GW : 包装响应
GW-->>Node : 返回 payload 或错误

详细组件分析

命令分发与注册机制(NodeHandler 系统)

  • 命令注册
    • 通过 InvokeCommandRegistry 查找命令规格,包含命令名、是否要求前台、可用性条件
    • 可用性条件支持:Always、CameraEnabled、LocationEnabled、SmsAvailable、MotionActivityAvailable、MotionPedometerAvailable、DebugBuild
  • 分发流程
    • 若命令未知,返回 INVALID_REQUEST
    • 若命令要求前台但节点不在前台,返回 NODE_BACKGROUND_UNAVAILABLE
    • 根据可用性条件检查(如相机/位置/短信/运动),不满足则返回相应错误码
    • 路由到具体处理器执行,A2UI 需要先确保 Canvas 可用与 A2UI 主机就绪
  • 错误处理
    • 统一包装为 GatewaySession.InvokeResult,包含 ok、payload 或 error(code/message)
flowchart TD
Start(["收到 node.invoke"]) --> Lookup["查找命令规格"]
Lookup --> Unknown{"未知命令?"}
Unknown --> |是| ErrUnknown["返回 INVALID_REQUEST"]
Unknown --> |否| Foreground{"需要前台?"}
Foreground --> |是且不在前台| ErrBg["返回 NODE_BACKGROUND_UNAVAILABLE"]
Foreground --> |否| Avail["检查可用性条件"]
Avail --> Denied{"条件不满足?"}
Denied --> |是| ErrCond["返回对应错误码"]
Denied --> |否| Route["路由到具体处理器"]
Route --> Exec["执行并返回结果"]
Exec --> End(["结束"])
ErrUnknown --> End
ErrBg --> End
ErrCond --> End

设备权限与状态(DeviceHandler)

  • 设备状态:电池、热状态、存储、网络连通性、耗电模式、uptime
  • 设备信息:设备名、型号标识、系统版本、应用版本/构建号、语言区域
  • 权限状态:相机、麦克风、位置、短信、通知监听、通知、相册、通讯录、日历、运动
  • 健康度:内存压力、电池状态/充电类型、温度、电流、Doze/LowPower 模式、安全补丁级别
classDiagram
class DeviceHandler {
+handleDeviceStatus(params) InvokeResult
+handleDeviceInfo(params) InvokeResult
+handleDevicePermissions(params) InvokeResult
+handleDeviceHealth(params) InvokeResult
-statusPayloadJson() String
-infoPayloadJson() String
-permissionsPayloadJson() String
-healthPayloadJson() String
}

相机控制(CameraHandler)

  • 列表:列举可用相机设备
  • 拍照:触发闪光、HUD 提示、捕获并返回 base64 图像
  • 录像:可选包含外部音频,限制最大负载,过大则删除临时文件并返回 PAYLOAD_TOO_LARGE
  • 错误处理:将异常映射为错误码与用户可读消息
sequenceDiagram
participant GW as "网关"
participant Disp as "InvokeDispatcher"
participant Cam as "CameraHandler"
GW->>Disp : node.invoke(camera.snap/clip)
Disp->>Cam : handleSnap/handleClip
Cam->>Cam : 触发 HUD/闪光/开始录制
Cam-->>Disp : 成功返回 base64/元数据
Disp-->>GW : InvokeResult.ok

联系人管理(ContactsHandler)

  • 搜索:支持查询关键字与数量上限,返回联系人列表
  • 新增:支持姓名、组织、电话、邮箱,通过批量插入写入系统通讯录
  • 权限:读取需 READ_CONTACTS,写入需 WRITE_CONTACTS;无权限直接返回 CONTACTS_PERMISSION_REQUIRED
flowchart TD
S(["contacts.search"]) --> CheckPerm["检查 READ_CONTACTS 权限"]
CheckPerm --> |无| ErrPerm["返回 CONTACTS_PERMISSION_REQUIRED"]
CheckPerm --> |有| Query["查询联系人"]
Query --> Ok["返回 contacts[]"]
A(["contacts.add"]) --> CheckWrite["检查 WRITE_CONTACTS 权限"]
CheckWrite --> |无| ErrPermAdd["返回 CONTACTS_PERMISSION_REQUIRED"]
CheckWrite --> |有| Validate["校验参数姓名/组织/电话/邮箱至少一项"]
Validate --> |无效| ErrInvalid["返回 CONTACTS_INVALID"]
Validate --> |有效| Insert["批量插入系统通讯录"]
Insert --> OkAdd["返回新增联系人"]

通知处理(NotificationsHandler)

  • 快照:读取通知快照(若启用监听但未连接,尝试重新绑定)
  • 动作:支持 open、dismiss、reply,reply 需要 replyText
  • 失败:返回 UNAVAILABLE 或具体错误码
sequenceDiagram
participant GW as "网关"
participant Disp as "InvokeDispatcher"
participant Noti as "NotificationsHandler"
GW->>Disp : node.invoke(notifications.list/actions)
Disp->>Noti : handleNotificationsList/handleNotificationsActions
Noti->>Noti : 读取快照/必要时重绑
Noti-->>Disp : 返回快照或执行结果
Disp-->>GW : InvokeResult

短信发送(SmsHandler)

  • 参数解析后委托底层 SmsManager 执行
  • 错误映射:将内部错误字符串按冒号前缀提取为错误码,返回 SMS_SEND_FAILED 默认码

位置服务(LocationHandler)

  • 权限:需要粗/精定位之一;前台运行时才允许
  • 精度:precise/coarse/balanced,受精确权限与系统设置影响
  • 超时:默认 10 秒,范围 1–60 秒
  • 异常:超时返回 LOCATION_TIMEOUT,其他异常返回 LOCATION_UNAVAILABLE

A2UI 交互(A2UIHandler)

  • 主机解析:优先节点 Canvas 主机,否则回退到运营者主机
  • 就绪检测:导航到 A2UI 页面并轮询检查 ready 标志
  • 消息应用:支持 messages 数组或 jsonl 字段,严格校验 v0.8 消息格式

网关命令策略与平台默认

  • 平台默认命令集:Android 默认开放 Canvas、Camera、Location、通知、系统通知、设备信息/状态/权限/健康、联系人、日历、提醒、相册、运动等命令
  • 危险命令:相机拍照/录像、屏幕录制、联系人新增、日历新增、提醒新增、短信发送等需显式允许
  • 声明校验:命令必须同时在节点声明的 commands 列表中

设备发现、蓝牙配对与网络通信

  • 设备发现:Android 使用 mDNS/NSD 发现 _openclaw-gw._tcp,支持本地与单播 DNS-SD(跨网络场景)
  • 蓝牙配对:通过网关握手阶段触发配对请求,未配对且非静默时拒绝连接并提示
  • 网络通信:WebSocket 连接,支持 Token/密码认证与 TLS

依赖关系分析

  • 节点侧
    • InvokeDispatcher 依赖各 Handler 与 A2UIHandler,受 InvokeCommandRegistry 的规格约束
    • 各 Handler 依赖系统服务(相机、联系人、通知、位置、短信等)
  • 网关侧
    • node-command-policy.ts 决定命令允许与否,结合节点声明与配置
    • 测试用例覆盖 Android 节点能力矩阵,验证命令返回结构与错误码
graph LR
Reg["InvokeCommandRegistry"] --> Disp["InvokeDispatcher"]
Disp --> DH["DeviceHandler"]
Disp --> CH["CameraHandler"]
Disp --> CNH["ContactsHandler"]
Disp --> NH["NotificationsHandler"]
Disp --> SH["SmsHandler"]
Disp --> LH["LocationHandler"]
Disp --> AH["A2UIHandler"]
Policy["node-command-policy.ts"] --> Disp
Test["android-node.capabilities.live.test.ts"] --> Disp

性能考量

  • 相机录视频负载限制:超过阈值会删除临时文件并返回错误,避免大包导致传输失败
  • 前台限制:Canvas/A2UI/相机/录屏等命令仅在前台可用,减少后台资源占用
  • 通知监听:若监听未连接,自动尝试重绑,降低用户干预成本
  • 网络发现:本地与单播 DNS-SD 双通道,提升跨网络发现成功率

网关通信

Android 网关通信相关代码主要位于 Android 应用模块中,核心类为 GatewaySession 与 DeviceAuthPayload;同时在后端/通用层提供协议定义与校验工具。

graph TB
subgraph "Android 应用"
GS["GatewaySession.kt<br/>会话管理/连接/消息处理"]
DAP["DeviceAuthPayload.kt<br/>认证载荷构建/归一化"]
end
subgraph "通用协议"
PI["protocol/index.ts<br/>帧校验/常量导出"]
SCHEMA["protocol/schema.ts<br/>协议模式入口"]
end
GS --> DAP
GS --> PI
PI --> SCHEMA

核心组件

  • GatewaySession(Android)
    • 负责 WebSocket 连接、消息收发、RPC 请求/响应、事件分发、节点调用请求处理、TLS 配置与指纹回调、Canvas URL 规范化等
    • 内部 Connection 类封装单次连接细节,包括握手、认证、心跳与断线处理
  • DeviceAuthPayload(Android)
    • 构建 v3 设备认证载荷字符串,统一元数据字段大小写规则,便于跨运行时一致性校验
  • 协议与校验(通用)
    • 提供帧类型、参数 Schema、AJV 校验器、协议版本常量与错误码导出

架构总览

Android 端通过 OkHttp WebSocket 客户端发起连接,按“握手挑战—连接参数—认证—会话建立”的顺序完成握手;随后进入消息循环,区分“响应帧”和“事件帧”,并支持节点侧向客户端发起的“invoke 请求”。

sequenceDiagram
participant App as "Android 应用"
participant WS as "OkHttp WebSocket"
participant Srv as "网关服务器"
App->>WS : "建立 WebSocket 连接"
WS-->>App : "onOpen()"
App->>Srv : "等待 connect.challenge 事件"
Srv-->>App : "事件 : connect.challenge { nonce }"
App->>App : "构造 connect 参数 + 设备签名"
App->>Srv : "RPC : connect"
Srv-->>App : "响应 : connect 成功 + 会话信息"
App->>App : "保存设备令牌/Canvas URL/会话键"
App->>Srv : "心跳/事件/请求/响应 循环"

组件详解

GatewaySession 会话管理

  • 连接与生命周期
    • 支持 connect/disconnect/reconnect,内部使用协程与互斥锁保证并发安全
    • 运行循环按指数回退策略重连,最大延迟上限控制
  • 消息处理
    • onMessage 解析 JSON 帧,区分 "res"(响应)与 "event"(事件)
    • pending 映射用于匹配请求 ID 与响应
  • 认证与握手
    • 等待 connect.challenge 事件提取 nonce,随后发送 connect RPC
    • 可选携带 token/password 与设备签名(公钥、签名、时间戳、nonce)
  • 节点调用
    • 接收 "node.invoke.request" 事件,调用应用提供的处理器,再以 "node.invoke.result" 回_ack_
  • Canvas URL 规范化
    • 根据连接是否 TLS 以及返回的 canvasHostUrl,修正 scheme/port/path/query/fragment
classDiagram
class GatewaySession {
+connect(endpoint, token, password, options, tls)
+disconnect()
+reconnect()
+request(method, paramsJson, timeoutMs)
+sendNodeEvent(event, payloadJson)
+refreshNodeCanvasCapability(timeoutMs)
}
class Connection {
+connect()
+request(method, params, timeoutMs)
+awaitClose()
-sendConnect(nonce)
-handleMessage(text)
-handleEvent(frame)
-handleResponse(frame)
-handleInvokeEvent(payloadJson)
-sendInvokeResult(id, nodeId, result, timeoutMs)
}
GatewaySession --> Connection : "持有并管理"

DeviceAuthPayload 认证机制

  • 载荷格式
    • v3 版本字符串由固定字段拼接,字段包括版本、设备 ID、客户端 ID、模式、角色、作用域列表、签名时间、令牌、nonce、平台、设备系列
    • 平台与设备系列字段进行大小写归一化(仅 ASCII A-Z 转小写),确保跨运行时一致性
  • 使用场景
    • 在 connect 参数中生成设备签名,随同公钥一起提交给网关,由网关验证签名与时间戳
flowchart TD
Start(["开始"]) --> BuildFields["拼接字段: v3|deviceId|clientId|mode|role|scopes|signedAtMs|token|nonce|platform|deviceFamily"]
BuildFields --> Normalize["平台/设备系列字段大小写归一化"]
Normalize --> Join["以'|'连接为最终载荷字符串"]
Join --> Sign["使用设备私钥对载荷签名"]
Sign --> Done(["结束"])

协议常量与校验(通用)

  • 协议版本
    • 通过 schema 导入统一导出协议版本常量,客户端在 connect 参数中声明 min/maxProtocol
  • 帧与参数校验
    • 使用 AJV 编译各 Schema,提供 validateXxx 函数用于请求/响应/事件/参数的运行时校验
    • formatValidationErrors 将校验错误格式化为可读字符串
  • 错误码与帧类型
    • 导出 ErrorCodes、GatewayFrame、RequestFrame、ResponseFrame、EventFrame 等类型与校验器
graph LR
IDX["protocol/index.ts"] --> SCH["protocol/schema.ts"]
IDX --> VALID["AJV 校验器"]
IDX --> CONST["协议常量/版本"]

心跳检测与保活

  • 服务端心跳
    • 网关定期下发 "tick" 事件,客户端收到后更新最近心跳时间
  • 客户端保活
    • OkHttp 客户端设置 pingInterval,默认 30 秒;若长时间无消息,可结合业务层 watchdog 强制重连(参考其他平台实现)

消息序列化与反序列化

  • 发送
    • request 方法构造 "req" 帧,序列化为 JSON 后通过 WebSocket 发送
  • 接收
    • onMessage 解析文本为 JSON 对象,根据 "type" 分派到 handleResponse 或 handleEvent
    • 响应帧通过 pending 映射匹配请求 ID,超时抛出异常
  • 节点调用结果
    • invoke 结果以 "node.invoke.result" RPC 返回,支持 payload 与 error 字段

连接配置与 TLS

  • TLS 配置
    • 可选 SSL Socket Factory 与 Hostname Verifier,支持自定义证书指纹校验回调
  • Ping 与超时
    • 写超时 60 秒,读超时无限制,ping 间隔 30 秒
  • Canvas URL 规范化
    • 根据连接是否 TLS 修正 https/scheme 与端口,保留路径/查询/片段

节点调用(GatewaySessionInvoke)

  • 事件到 RPC 的桥接
    • 当收到 "node.invoke.request" 事件时,解析参数(id/nodeId/command/paramsJSON/timeoutMs),调用应用提供的处理器
    • 处理完成后以 "node.invoke.result" 发送结果,超时范围在 15–120 秒之间
  • 测试场景
    • 单测覆盖握手、事件分发、结果回传与关闭流程
sequenceDiagram
participant GS as "GatewaySession.Connection"
participant App as "应用处理器"
GS->>GS : "接收事件 : node.invoke.request"
GS->>App : "调用处理器(InvokeRequest)"
App-->>GS : "返回 InvokeResult"
GS->>GS : "构造 node.invoke.result 参数"
GS-->>GS : "发送 RPC : node.invoke.result"

依赖关系分析

  • Android 端
    • GatewaySession 依赖 OkHttp WebSocket、Kotlinx Serialization、协程与互斥锁
    • DeviceAuthPayload 作为纯函数对象,被 Connection 构造 connect 参数时调用
  • 通用层
    • protocol/index.ts 导出 AJV 校验器与协议常量,供服务端/客户端共享
    • schema.ts 汇总各类 Schema,形成强类型协议模型
graph TB
A["GatewaySession.kt"] --> B["DeviceAuthPayload.kt"]
A --> C["protocol/index.ts"]
C --> D["protocol/schema.ts"]

性能与可靠性

  • 指数回退重连
    • 连接失败时按 1.7 指数增长延迟,上限 8 秒,避免风暴式重试
  • 超时与确认
    • connect RPC 超时 12 秒,invoke 结果确认超时在 15–120 秒区间
  • 心跳与保活
    • OkHttp ping 间隔 30 秒;业务层可结合 watchdog 在长时间无消息时触发重连
  • TLS 与指纹
    • 可配置自定义证书链与主机名校验,并在连接建立时回调指纹,便于运维审计

相机与屏幕录制

Android 相关实现集中在 apps/android/app 模块,核心文件包括:

  • 节点命令处理器:CameraHandler、PhotosHandler
  • 相机采集与录制:CameraCaptureManager
  • 状态与权限:CameraHudState、PermissionRequester
  • 辅助工具:JpegSizeLimiter
  • 文档参考:docs/platforms/android.md
graph TB
subgraph "Android 应用"
CH["CameraHandler<br/>节点命令入口"]
CCM["CameraCaptureManager<br/>相机采集/录制"]
PH["PhotosHandler<br/>相册读取"]
PR["PermissionRequester<br/>权限申请"]
HLS["CameraHudState<br/>HUD 状态模型"]
JSL["JpegSizeLimiter<br/>JPEG 尺寸限制器"]
end
CH --> CCM
CH --> HLS
CH --> PR
PH --> PR
CCM --> PR
CCM --> JSL

核心组件

  • CameraHudState:定义相机 HUD 的状态类型(拍照、录制、成功、错误),用于 UI 提示
  • CameraHandler:节点命令入口,负责 camera.list、camera.snap、camera.clip 的调用与结果封装
  • CameraCaptureManager:实际执行相机操作,含权限检查、设备选择、拍照与录制、文件输出与事件监听
  • PhotosHandler:读取系统相册最新图片,按预算压缩为 JPEG 并返回 base64
  • PermissionRequester:统一的权限申请与引导,支持理由说明与设置页跳转
  • JpegSizeLimiter:在尺寸与质量之间迭代压缩,确保输出不超过上限

架构总览

下图展示了从节点命令到相机采集与录制的整体流程,以及与权限系统的交互。

sequenceDiagram
participant Node as "节点命令"
participant Handler as "CameraHandler"
participant Manager as "CameraCaptureManager"
participant Perm as "PermissionRequester"
participant Cam as "相机系统(CamerX)"
participant UI as "HUD 状态"
Node->>Handler : 调用 camera.snap 或 camera.clip
Handler->>UI : 显示提示(拍照/录制)
alt 需要相机权限
Handler->>Perm : 请求相机权限
Perm-->>Handler : 返回授权结果
end
Handler->>Manager : 执行 snap/clip
Manager->>Cam : 绑定生命周期/选择相机/开始录制
Cam-->>Manager : 回调事件(Finalize/状态)
Manager-->>Handler : 返回结果(照片/文件)
Handler->>UI : 显示成功/错误提示
Handler-->>Node : 返回 JSON 结果

组件详解

相机状态管理:CameraHudState

  • 定义状态类型:Photo(拍照)、Recording(录制中)、Success(成功)、Error(错误)
  • 数据结构包含 token、kind、message,用于 UI 层渲染与自动隐藏

相机处理器:CameraHandler

职责与流程:

  • 设备列表:调用 CameraCaptureManager.listDevices,返回设备数组
  • 拍照:显示 HUD、触发闪光、调用 snap,返回 base64 JPEG;失败时显示错误 HUD
  • 录制:显示 HUD、可选启用外部音频、调用 clip,将 mp4 文件读入内存并 base64 编码返回;超过阈值则删除临时文件并报错

关键行为:

  • 参数解析:includeAudio、durationMs、deviceId、facing、quality、maxWidth
  • 负载限制:对 clip 的二进制大小进行上限检查,避免 WebSocket 超限
  • HUD 生命周期:成功/错误提示与自动隐藏时间

相机采集与录制:CameraCaptureManager

职责与流程:

  • 设备枚举:通过 ProcessCameraProvider 获取可用摄像头并映射为设备信息
  • 权限保障:ensureCameraPermission / ensureMicPermission
  • 拍照:
    • 解析参数:facing、quality、maxWidth、deviceId
    • 绑定生命周期并拍摄 JPEG,读取 EXIF 方向并旋转,按 maxWidth 缩放
    • 使用 JpegSizeLimiter 控制 JPEG 大小,返回 JSON 字符串
  • 录制:
    • 解析参数:facing、durationMs、includeAudio、deviceId
    • 设置最低质量以减小文件体积
    • 绑定 Preview 与 VideoCapture,预热后开始录制,延时后停止
    • 监听 Finalize 事件,超时或失败时清理临时文件并抛出异常
    • 返回 File 与元数据(时长、是否含音频)

辅助工具:

  • takeJpegWithExif:异步拍摄 JPEG 并返回字节与 EXIF 方向
  • cameraDeviceInfoOrNull / cameraIdOrNull:从 CameraInfo 解析设备信息
  • JpegSizeLimiter:在尺寸与质量间迭代压缩,确保不超过上限

相册处理:PhotosHandler

职责与流程:

  • 权限检查:根据系统版本选择 READ_MEDIA_IMAGES 或 READ_EXTERNAL_STORAGE
  • 查询策略:按拍摄时间与添加时间降序查询最近图片
  • 解码与缩放:按最大宽度计算 inSampleSize 并解码,必要时缩放
  • 压缩与预算:使用预算约束编码 JPEG,逐张评估 base64 长度,累计不超过总预算
  • 返回结构:每张图片包含 format、base64、width、height、createdAt

权限管理:PermissionRequester

  • 支持多权限一次性申请
  • 对需要理由的权限弹窗说明用途
  • 对被拒绝且不再提示的权限,引导用户前往应用设置开启
  • 内部互斥与超时控制,保证并发安全

JPEG 尺寸限制器:JpegSizeLimiter

  • 输入:初始宽高、起始质量、最大字节数
  • 策略:优先降低质量,再逐步缩小尺寸,直到满足上限
  • 输出:最终字节、宽高、质量

屏幕录制(跨平台对比)

  • iOS 实现要点:
    • 计算配置:时长、帧率、音频开关、输出路径
    • 启动/停止/写入:分阶段回调,准备写入器、处理视频样本、完成写入
    • FPS 采样间隔:按目标帧率去抖,避免过量写入
  • macOS 实现要点:
    • 可选音频输入配置(AAC)
    • 流停止错误记录与日志

依赖关系分析

  • CameraHandler 依赖 CameraCaptureManager、PermissionRequester、CameraHudState
  • CameraCaptureManager 依赖 CameraX(ProcessCameraProvider、ImageCapture、VideoCapture)、ExifInterface、JpegSizeLimiter
  • PhotosHandler 依赖系统媒体存储 ContentResolver、Bitmap 解码与压缩
  • PermissionRequester 依赖 Android ActivityResultLauncher 与系统设置页面
classDiagram
class CameraHandler {
+handleList(params)
+handleSnap(params)
+handleClip(params)
}
class CameraCaptureManager {
+listDevices()
+snap(params)
+clip(params)
}
class PermissionRequester {
+requestIfMissing(perms)
}
class PhotosHandler {
+handlePhotosLatest(params)
}
class JpegSizeLimiter {
+compressToLimit(...)
}
class CameraHudState {
+token
+kind
+message
}
CameraHandler --> CameraCaptureManager : "调用"
CameraHandler --> PermissionRequester : "请求权限"
CameraHandler --> CameraHudState : "更新HUD"
CameraCaptureManager --> JpegSizeLimiter : "压缩JPEG"
PhotosHandler --> PermissionRequester : "读取相册需权限"

性能考量

  • 拍照路径
    • 预热与方向:先旋转再缩放,减少重复变换开销
    • 压缩预算:JPEG 压缩与尺寸缩放双管齐下,确保 payload 不超限
  • 录制路径
    • 最低质量:优先降低质量而非分辨率,兼顾体积与清晰度
    • 预览绑定:强制绑定 Preview 以激活编码管线,避免无有效数据
    • 超时与清理:录制完成后等待 Finalize,超时或失败及时删除临时文件
  • 相册读取
    • inSampleSize 估算与按预算编码,避免一次性加载过大位图
    • 累计预算控制,保证多图返回不越界

应用架构

Android 应用位于 apps/android/app 模块,采用 Kotlin + Jetpack Compose UI + OkHttp WebSocket 通信的现代 Android 技术栈。核心目录与职责概览:

  • app/src/main/java/ai/openclaw/app
    • 入口与生命周期:NodeApp、MainActivity、NodeForegroundService
    • 状态与视图模型:MainViewModel
    • 核心运行时:NodeRuntime 及其子系统(网关会话、Canvas、相机、语音等)
    • UI 层:RootScreen 与各功能页(Compose)
    • 配置与清单:AndroidManifest.xml、build.gradle.kts
  • app/src/main/res:资源与主题
  • 测试:app/src/test/java 下按功能域分层测试
graph TB
subgraph "应用进程"
NA["NodeApp<br/>Application"]
MA["MainActivity<br/>ComponentActivity"]
VM["MainViewModel<br/>AndroidViewModel"]
NS["NodeForegroundService<br/>Service"]
end
subgraph "运行时核心"
NR["NodeRuntime<br/>核心编排"]
GS["GatewaySession<br/>操作端/节点端"]
CC["CanvasController<br/>WebView 承载"]
CM["CameraCaptureManager<br/>CameraX 封装"]
end
NA --> NR
MA --> VM
VM --> NR
MA --> NS
NR --> GS
NR --> CC
NR --> CM

核心组件

  • NodeApp:应用入口,负责严格模式调试与延迟初始化 NodeRuntime 单例
  • MainActivity:承载 Compose UI,绑定 MainViewModel,处理权限与系统窗口标志,延后启动前台服务
  • MainViewModel:将 NodeRuntime 的大量 StateFlow 暴露给 UI,并转发用户设置与连接控制命令
  • NodeRuntime:应用核心运行时,聚合网关会话、Canvas、相机、位置、短信、语音等子系统,统一状态与事件分发
  • NodeForegroundService:前台服务,根据 NodeRuntime 状态流动态更新通知
  • GatewaySession:封装 WebSocket 连接、鉴权、RPC 请求/响应、事件分发与自动重连
  • CanvasController:WebView 容器与调试状态注入,提供快照与脚本执行能力
  • CameraCaptureManager:基于 CameraX 的拍照/视频录制封装,含权限与 EXIF 方向处理

架构总览

应用采用“单例运行时 + 响应式状态流”的架构模式:

  • NodeApp.lazy 持有 NodeRuntime,避免冷启动路径冗余
  • NodeRuntime 使用协程与 StateFlow 组织多源状态(连接、Canvas、相机、语音、聊天等),并通过 GatewaySession 与远端网关保持双向通信
  • MainActivity 仅承担 UI 与生命周期职责,通过 MainViewModel 访问运行时能力
  • NodeForegroundService 以前台服务形式常驻,实时反映连接状态与麦克风监听状态
sequenceDiagram
participant App as "NodeApp"
participant Runtime as "NodeRuntime"
participant Operator as "GatewaySession(操作端)"
participant Node as "GatewaySession(节点端)"
participant Service as "NodeForegroundService"
App->>Runtime : lazy 初始化
Note over Runtime : 启动协程作用域并注册各子系统
Runtime->>Operator : 构建连接参数并发起连接
Runtime->>Node : 构建连接参数并发起连接
Operator-->>Runtime : 连接成功/失败回调
Node-->>Runtime : 连接成功/失败回调
Runtime-->>Service : 状态流变化连接/服务器名/麦克风
Service-->>Service : 更新通知

详细组件分析

启动流程与生命周期

  • 应用启动
    • AndroidManifest 指定 Application 为 NodeApp,Activity 为 MainActivity
    • NodeApp 在 onCreate 中启用严格模式(调试构建)并延迟初始化 NodeRuntime
  • MainActivity 生命周期
    • onCreate:禁用系统窗口装饰适配、初始化 PermissionRequester、绑定相机/短信权限请求器、设置 UI 主题与根屏幕、在首帧后延时启动 NodeForegroundService
    • onStart/onStop:切换前台状态,影响 NodeRuntime 内部的语音会话与外部音频捕获标记
  • 前台服务
    • NodeForegroundService 在 onCreate 中创建通知通道并首次显示“正在启动”通知
    • 通过组合 NodeRuntime 的多个状态流,动态更新标题与内容;支持从通知触发断开连接
flowchart TD
Start(["应用启动"]) --> AppInit["NodeApp.onCreate()<br/>启用严格模式"]
AppInit --> RuntimeInit["NodeApp.runtime.lazy 初始化"]
RuntimeInit --> ActivityInit["MainActivity.onCreate()"]
ActivityInit --> BindPerms["绑定权限请求器"]
ActivityInit --> SetUI["设置主题与根屏幕"]
ActivityInit --> DelayStart["首帧后延时启动前台服务"]
ActivityInit --> Foreground["NodeForegroundService.start()"]
Foreground --> CombineState["合并状态流<br/>连接/服务器/麦克风"]
CombineState --> UpdateNotify["更新通知"]
ActivityInit --> OnStart["onStart() 设置前台=true"]
ActivityInit --> OnStop["onStop() 设置前台=false"]

状态管理与 MainViewModel

  • MainViewModel 作为 AndroidViewModel,持有 NodeApp.runtime 并直接暴露 NodeRuntime 的大量 StateFlow(连接状态、Canvas 状态、相机/位置/麦克风/扬声器、聊天状态等)
  • 提供 setter 方法将用户设置与连接控制命令转发至 NodeRuntime,实现 UI 对运行时的可控访问
  • 通过 viewModels() 在 MainActivity 中获取实例,确保进程内共享与生命周期感知
classDiagram
class MainViewModel {
+canvas : CanvasController
+camera : CameraCaptureManager
+sms : SmsManager
+isConnected : StateFlow<Boolean>
+statusText : StateFlow<String>
+chat* 系列状态
+set*() 用户设置方法
+connect()/disconnect()
+sendChat()/abortChat()
}
class NodeRuntime {
+canvas : CanvasController
+camera : CameraCaptureManager
+sms : SmsManager
+gateways/statusText/pendingGatewayTrust
+chat* 系列状态
+set*() 用户设置
+connect()/disconnect()
+sendChat()/abortChat()
}
MainViewModel --> NodeRuntime : "委托调用"

NodeRuntime:运行时核心

  • 协程与作用域:使用 SupervisorJob + Dispatchers.IO 管理子系统任务,保证异常不扩散
  • 子系统聚合:Canvas、相机、位置、短信、通知、系统、照片、联系人、日历、运动、A2UI、Invoke 分发器等
  • 网关会话:维护两个 GatewaySession(操作端与节点端),分别处理连接、断开、事件与 RPC 请求
  • 状态流:对外暴露大量只读 StateFlow,内部通过 MutableStateFlow 维护可变状态并进行去抖与合并
  • 自动连接:依据偏好设置与发现列表,自动连接可信网关(基于存储的 TLS 指纹)
  • Canvas A2UI:支持从 WebView 触发 agent.request 并回传状态反馈
classDiagram
class NodeRuntime {
-scope : CoroutineScope
-canvas : CanvasController
-camera : CameraCaptureManager
-location : LocationCaptureManager
-sms : SmsManager
-discovery : GatewayDiscovery
-operatorSession : GatewaySession
-nodeSession : GatewaySession
-invokeDispatcher : InvokeDispatcher
+connect()/disconnect()
+requestCanvasRehydrate()
+handleCanvasA2UIActionFromWebView()
}
class GatewaySession {
+connect()
+disconnect()
+reconnect()
+request()
+sendNodeEvent()
}
class CanvasController {
+attach()/detach()
+navigate()
+eval()/snapshot*
}
NodeRuntime --> CanvasController : "持有"
NodeRuntime --> GatewaySession : "两个会话"
NodeRuntime --> CanvasController : "A2UI 交互"

UI 与导航

  • RootScreen:根据 onboardingCompleted 决定展示引导流程或主标签页
  • MainActivity:设置系统窗口装饰、权限请求器、绑定相机/短信权限、根据生命周期控制“防休眠”标志位、渲染主题与根屏幕
  • NodeForegroundService:根据运行时状态流动态更新通知,支持从通知断开连接
sequenceDiagram
participant UI as "RootScreen"
participant VM as "MainViewModel"
participant NR as "NodeRuntime"
UI->>VM : collect onboardingCompleted
alt 未完成
UI->>UI : 显示引导流程
else 已完成
UI->>UI : 显示主标签页
end
VM->>NR : set*()/connect()/sendChat()

网络与安全

  • GatewaySession 使用 OkHttp WebSocket 实现连接、鉴权、RPC 请求与事件分发
  • 支持 TLS 参数解析与指纹校验,首次连接时捕获指纹并提示用户验证,随后持久化到偏好中
  • 自动重连策略随失败次数指数退避,上限保护
flowchart TD
Connect["发起连接"] --> Challenge["接收挑战 nonce"]
Challenge --> Auth["构造 connect 参数<br/>签名/公钥/角色/权限"]
Auth --> Send["发送 connect 请求"]
Send --> Resp{"响应 ok?"}
Resp -- 是 --> Ready["建立会话<br/>保存 canvasHostUrl/mainSessionKey"]
Resp -- 否 --> Fail["抛出错误并断开"]
Ready --> Event["事件/请求分发"]
Event --> Retry["异常/断开后按指数退避重连"]

设备能力与媒体

  • CanvasController:WebView 容器,支持导航、调试状态注入、JS 评估与图片快照(PNG/JPEG)
  • CameraCaptureManager:基于 CameraX 的拍照与视频录制,含 EXIF 方向旋转、质量压缩、最大尺寸限制与权限检查
classDiagram
class CanvasController {
+attach()/detach()
+navigate()
+eval()
+snapshotPngBase64()
+snapshotBase64()
}
class CameraCaptureManager {
+snap()
+clip()
+listDevices()
-ensureCameraPermission()
-ensureMicPermission()
}
NodeRuntime --> CanvasController : "持有"
NodeRuntime --> CameraCaptureManager : "持有"

依赖关系分析

  • 模块耦合
    • NodeApp 与 NodeRuntime:单例持有,低耦合高内聚
    • MainActivity 与 MainViewModel:通过 ViewModelProvider 解耦,UI 不直接依赖运行时
    • NodeRuntime 与子系统:通过组合模式聚合,职责清晰
  • 外部依赖
    • Jetpack Compose、Material3、Navigation
    • OkHttp WebSocket、CameraX、Kotlinx Serialization、Kotlinx Coroutines
    • BouncyCastle、CommonMark 等
graph LR
NA["NodeApp"] --> NR["NodeRuntime"]
MA["MainActivity"] --> VM["MainViewModel"]
VM --> NR
NR --> GS["GatewaySession"]
NR --> CC["CanvasController"]
NR --> CM["CameraCaptureManager"]
MA --> NS["NodeForegroundService"]

性能考量

  • 启动路径优化:MainActivity 在首帧后才启动前台服务,减少冷启动阻塞
  • 状态流去抖与合并:NodeRuntime 使用 combine + distinctUntilChanged 控制 UI 更新频率
  • 图片快照与压缩:CanvasController 与 CameraCaptureManager 对图片进行缩放与质量压缩,避免超大负载
  • 协程调度:IO 调度器用于网络与磁盘 IO,Main 调度器用于 UI 相关操作
  • 通知更新:NodeForegroundService 仅在状态变化时更新通知,降低系统开销

语音功能

Android 语音相关代码主要位于应用模块的 voice 包与根级配置类中,配合 UI 层的引导与权限请求,形成完整的语音工作流。

graph TB
subgraph "Android 应用"
A["VoiceWakeMode<br/>唤醒模式枚举"]
B["VoiceWakeManager<br/>唤醒监听器"]
C["WakeWords<br/>唤醒词工具"]
D["SecurePrefs<br/>安全偏好存储"]
E["VoiceWakeCommandExtractor<br/>命令提取器"]
F["TalkDirectiveParser<br/>指令解析器"]
G["TalkModeManager<br/>Talk 模式管理器"]
H["ElevenLabsStreamingTts<br/>流式 TTS"]
I["OnboardingFlow<br/>引导与权限"]
J["VoiceTabScreen<br/>语音标签页 UI"]
end
A --> D
C --> D
D --> B
D --> G
B --> E
G --> F
G --> H
I --> J
J --> G

核心组件

  • 语音唤醒模式:VoiceWakeMode 定义 off/foreground/always 三种模式,并提供从原始字符串解析的方法。
  • 唤醒词管理:WakeWords 提供解析、变更检测与清洗逻辑;SecurePrefs 负责持久化存储与默认值。
  • 唤醒监听:VoiceWakeManager 使用 Android SpeechRecognizer 进行持续监听,结合 VoiceWakeCommandExtractor 提取触发后的命令。
  • Talk 指令解析:TalkDirectiveParser 解析首行 JSON 指令,支持多键名别名映射,剥离后返回纯文本与未知键列表。
  • Talk 模式:TalkModeManager 实现录音、转写、对话、TTS 播放与中断控制,支持 ElevenLabs 流式 TTS 与系统 TTS 双通道。
  • 流式 TTS:ElevenLabsStreamingTts 通过 WebSocket 接收实时音频,AudioTrack/PCM 或 MediaPlayer 播放。

架构总览

Android 语音功能采用“手动触发 + 云端转写 + 本地/云端 TTS”的混合架构。用户在语音标签页点击开始录音,TalkModeManager 启动 SpeechRecognizer,转写为文本后发送到网关,等待最终回复并进行 TTS 播放。若具备 ElevenLabs 凭证则优先使用其流式 TTS,否则回退系统 TTS。

sequenceDiagram
participant UI as "语音标签页 UI"
participant TM as "TalkModeManager"
participant SR as "SpeechRecognizer"
participant GW as "网关会话"
participant EL as "ElevenLabs"
participant SYS as "系统TTS"
UI->>TM : 开始录音
TM->>SR : 启动识别(云转写)
SR-->>TM : 部分/最终结果
TM->>GW : chat.send(带会话键)
GW-->>TM : agent 流事件/最终事件
alt 有 ElevenLabs 凭证
TM->>EL : 流式合成(WebSocket)
EL-->>TM : 音频流
TM-->>UI : 播放音频
else 回退
TM->>SYS : 文本转语音
SYS-->>TM : 语音输出
TM-->>UI : 播放语音
end

详细组件分析

语音唤醒模式与唤醒词管理

  • VoiceWakeMode:定义三种模式,提供字符串到枚举的解析,默认值为前台模式。
  • WakeWords:限制最大数量与长度,支持逗号分隔解析、变更检测与清洗。
  • SecurePrefs:持久化存储唤醒词列表与模式,提供默认唤醒词集合;加载时进行 JSON 解码与清洗。
classDiagram
class VoiceWakeMode {
+Off
+Foreground
+Always
+fromRawValue(raw)
}
class WakeWords {
+maxWords : Int
+maxWordLength : Int
+parseCommaSeparated(input)
+parseIfChanged(input, current)
+sanitize(words, defaults)
}
class SecurePrefs {
+setWakeWords(words)
+setVoiceWakeMode(mode)
+loadWakeWords()
+loadVoiceWakeMode()
}
VoiceWakeMode <.. SecurePrefs : "使用"
WakeWords <.. SecurePrefs : "清洗/校验"

语音唤醒监听与命令提取

  • VoiceWakeManager:封装 SpeechRecognizer 生命周期,处理错误与重启;监听部分/最终结果,调用 VoiceWakeCommandExtractor 提取命令。
  • VoiceWakeCommandExtractor:基于触发词正则匹配,提取触发词之后的自然语言命令,过滤空值与标点。
flowchart TD
Start(["开始监听"]) --> Listen["SpeechRecognizer 启动"]
Listen --> OnResult{"收到结果?"}
OnResult --> |否| Restart["延迟重启"]
OnResult --> |是| Extract["提取命令"]
Extract --> Valid{"命令有效?"}
Valid --> |否| Restart
Valid --> |是| Dispatch["派发命令回调"]
Dispatch --> Restart

Talk 指令解析机制

  • TalkDirectiveParser:解析首行 JSON 指令,支持多键名别名(如 voice_id、speakerBoost 等),记录未知键;剥离指令后返回纯文本与未知键列表。
flowchart TD
In(["输入文本"]) --> Split["按行分割"]
Split --> FindHead["定位首个非空行"]
FindHead --> IsObj{"是否为 JSON 对象?"}
IsObj --> |否| ReturnPlain["返回纯文本与空未知键"]
IsObj --> |是| Parse["解析对象字段"]
Parse --> MapKeys["键名归一化映射"]
MapKeys --> BuildDirective["构建指令对象"]
BuildDirective --> HasDirective{"存在有效字段?"}
HasDirective --> |否| ReturnPlain
HasDirective --> |是| Strip["移除首行与空行"]
Strip --> CollectUnknown["收集未知键"]
CollectUnknown --> Out(["返回指令+文本+未知键"])

Talk 模式:录音、转写、TTS 播放

  • 录音与转写:TalkModeManager 使用 SpeechRecognizer,启用云转写与静默窗口策略,避免过早结束。
  • 对话与订阅:支持 chat.subscribe 订阅事件流,缓存最终文本,减少轮询。
  • TTS 播放:优先 ElevenLabs 流式 TTS(WebSocket),失败或不支持时回退到文件下载播放或系统 TTS;支持音频焦点与中断控制。
sequenceDiagram
participant TM as "TalkModeManager"
participant SR as "SpeechRecognizer"
participant GW as "网关"
participant ST as "ElevenLabsStreamingTts"
participant AT as "AudioTrack/MediaPlayer"
participant SYS as "系统TTS"
TM->>SR : startListening(云转写)
SR-->>TM : 部分/最终结果
TM->>GW : chat.send + subscribe
GW-->>TM : 流式/最终事件
alt ElevenLabs 可用
TM->>ST : start + sendText
ST-->>TM : 音频流
TM->>AT : 播放
else 回退
TM->>SYS : speak
SYS-->>TM : 语音
TM->>AT : 播放
end

依赖关系分析

  • 组件耦合
    • VoiceWakeManager 依赖 WakeWords 与 VoiceWakeCommandExtractor,受 SecurePrefs 中的模式与触发词影响。
    • TalkModeManager 依赖 GatewaySession、ElevenLabsStreamingTts 与系统 TTS,内部维护状态流与播放令牌。
  • 外部依赖
    • Android SpeechRecognizer(云转写)
    • ElevenLabs API(流式 TTS)
    • Android AudioManager/AudioTrack/MediaPlayer(音频播放)
graph LR
SW["SecurePrefs"] --> VWM["VoiceWakeManager"]
WW["WakeWords"] --> VWM
VWE["VoiceWakeCommandExtractor"] --> VWM
VWM --> CMD["命令回调"]
TM["TalkModeManager"] --> SR["SpeechRecognizer"]
TM --> GW["GatewaySession"]
TM --> ELS["ElevenLabsStreamingTts"]
TM --> SYS["系统TTS"]

性能考虑

  • 转写与静默策略
    • 使用云转写模型,配合静默窗口参数,提升自然停顿后的识别稳定性。
    • 在播放期间可选择“说话时打断”以避免录音拾取播放音频导致的设备特定问题。
  • TTS 播放路径
    • 优先 WebSocket 流式 PCM,降低延迟;失败时降级为 MP3 文件下载播放,提高兼容性。
    • AudioTrack 缓冲区大小与最小缓冲区计算,避免 OEM 设备(如 OxygenOS/OnePlus)的 AudioTrack 写入问题。
  • 状态与资源管理
    • 使用播放令牌与生成器确保并发播放不会互相干扰;及时释放 MediaPlayer/AudioTrack 与 SpeechRecognizer。
    • 缓存最终聊天文本,减少历史查询开销。

Android 应用

Android 应用位于 apps/android 目录,采用 Gradle 多模块结构,核心模块为 app;测试与基准位于 benchmark、test 等目录。应用通过 Jetpack Compose 构建 UI,使用 OkHttp WebSocket 连接网关,结合 CameraX 实现相机与视频录制能力,并通过自研 NodeRuntime 统一调度各类节点命令与状态。

graph TB
subgraph "应用层"
MA["MainActivity<br/>生命周期与权限绑定"]
VM["MainViewModel<br/>状态与行为入口"]
RT["NodeRuntime<br/>运行时与会话调度"]
end
subgraph "节点处理层"
CAM["CameraCaptureManager<br/>拍照/录屏"]
DEVH["DeviceHandler<br/>设备信息/健康/权限"]
INV["InvokeDispatcher<br/>命令分发"]
A2UI["A2UIHandler<br/>Canvas/A2UI"]
end
subgraph "网关通信层"
GS["GatewaySession<br/>WebSocket/RPC"]
DISC["GatewayDiscovery<br/>发现/信任提示"]
end
subgraph "系统与权限"
PERM["PermissionRequester<br/>动态权限请求"]
SVC["NodeForegroundService<br/>前台服务"]
NLS["DeviceNotificationListenerService<br/>通知监听"]
end
MA --> VM
VM --> RT
RT --> CAM
RT --> DEVH
RT --> INV
RT --> A2UI
RT --> GS
RT --> DISC
MA --> PERM
MA --> SVC
MA --> NLS

核心组件

  • MainActivity:负责应用启动、权限请求器绑定、前台服务启动时机、保持屏幕常亮等。
  • MainViewModel:桥接 UI 与 NodeRuntime,暴露状态流与操作方法。
  • NodeRuntime:统一运行时,管理网关会话、节点命令分发、Canvas/A2UI、语音、聊天、设备能力等。
  • PermissionRequester:封装动态权限请求流程,支持理由对话与设置页引导。
  • CameraCaptureManager:基于 CameraX 的拍照与录屏能力,支持参数化控制与权限校验。
  • DeviceHandler:提供设备状态、信息、权限与健康度查询。
  • GatewaySession:基于 OkHttp 的 WebSocket 会话,负责连接、RPC 请求、事件分发与重连。

架构总览

应用采用“运行时统一调度 + 节点处理器 + 网关会话”的分层架构。NodeRuntime 将 UI 层与底层系统能力解耦,通过 InvokeDispatcher 将命令路由到具体处理器(如 CameraHandler、LocationHandler、DeviceHandler 等),并通过 GatewaySession 与网关建立长连接,实现命令调用、事件推送与 Canvas/A2UI 交互。

sequenceDiagram
participant UI as "UI/MainActivity"
participant VM as "MainViewModel"
participant RT as "NodeRuntime"
participant GS as "GatewaySession"
participant DISP as "InvokeDispatcher"
participant H as "各处理器(如Camera/Device)"
UI->>VM : 用户操作/状态订阅
VM->>RT : 调用连接/断开/命令
RT->>GS : 建立/维护会话
RT->>DISP : 分发命令(command,params)
DISP->>H : 路由到对应处理器
H-->>DISP : 返回结果或错误
DISP-->>RT : 汇总结果
RT-->>GS : 发送 node.invoke.result 或 node.event
GS-->>UI : 推送事件/状态更新

详细组件分析

设备控制与命令执行

  • 运行时调度:NodeRuntime 初始化多个处理器(相机、位置、设备、通知、系统、照片、联系人、日历、运动、短信、A2UI、调试等),并通过 InvokeDispatcher 将命令路由至对应处理器。
  • 命令分发:InvokeDispatcher 在处理前检查前置条件(如前台状态、相机启用、位置模式、短信可用性、录音权限等),并根据处理器返回结果构造响应。
  • 网关会话:GatewaySession 负责 WebSocket 连接、RPC 请求、事件分发与重连策略,支持 TLS 参数与指纹校验,确保首次连接的信任提示与后续自动连接的安全性。
classDiagram
class NodeRuntime {
+gateways
+discoveryStatusText
+isConnected
+nodeConnected
+statusText
+camera
+location
+sms
+canvas
+connect(endpoint)
+disconnect()
+refreshGatewayConnection()
}
class InvokeDispatcher {
+handleInvoke(command,params)
}
class GatewaySession {
+connect(endpoint,token,password,options,tls)
+request(method,params,timeout)
+sendNodeEvent(event,payload)
+reconnect()
+disconnect()
}
NodeRuntime --> InvokeDispatcher : "分发命令"
NodeRuntime --> GatewaySession : "会话管理"

相机与屏幕录制

  • 权限与参数:支持 CAMERA、RECORD_AUDIO(可选)权限;参数包括 facing、quality、maxWidth、durationMs、deviceId、includeAudio 等。
  • 拍照:使用 CameraX ImageCapture,读取 EXIF 方向并旋转/缩放,压缩至 5MB 以内,返回 base64 JPEG。
  • 录屏:使用 VideoCapture + Recorder,最低质量以减小文件体积;需预热摄像头并提供空预览以激活编码器;支持带/不带音频录制。
  • 错误处理:超时与失败场景删除临时文件、释放资源并抛出明确异常。
flowchart TD
Start(["开始 clip/snap"]) --> CheckPerm["检查相机/麦克风权限"]
CheckPerm --> |缺失| RequestPerm["弹窗请求权限"]
RequestPerm --> |拒绝| ThrowErr["抛出权限不足错误"]
RequestPerm --> |允许| BindCam["绑定生命周期与相机选择器"]
BindCam --> Mode{"操作类型"}
Mode --> |snap| TakePhoto["拍照并读取EXIF方向"]
TakePhoto --> Rotate["按EXIF旋转位图"]
Rotate --> Scale["按maxWidth缩放"]
Scale --> Limit["压缩至5MB内(base64上限)"]
Limit --> ReturnSnap["返回JPEG payload"]
Mode --> |clip| Record["准备录制(可含音频)"]
Record --> Warm["预热摄像头1.5秒"]
Warm --> StartRec["开始录制并等待定时结束"]
StartRec --> StopRec["停止录制并等待完成事件"]
StopRec --> Finalize{"是否成功完成"}
Finalize --> |否| Clean["删除临时文件并抛错"]
Finalize --> |是| ReturnClip["返回文件路径/时长/音频标记"]

设备命令与权限管理

  • 设备状态/信息/健康:DeviceHandler 提供电池、存储、网络、内存、温度、压力等级等信息;权限状态汇总(相机、麦克风、位置、短信、通知监听、通知、相册、联系人、日历、运动等)。
  • 权限请求:PermissionRequester 支持多权限批量请求、理由对话、被拒后引导至系统设置页。
  • 后台限制:应用通过前台服务维持关键能力(数据同步、通知等),并在生命周期变化时调整行为(如前台切换时停止语音会话)。
classDiagram
class DeviceHandler {
+handleDeviceStatus(params)
+handleDeviceInfo(params)
+handleDevicePermissions(params)
+handleDeviceHealth(params)
}
class PermissionRequester {
+requestIfMissing(permissions,timeout)
-showRationaleDialog(permissions)
-showSettingsDialog(permissions)
}
class NodeRuntime {
+setForeground(value)
+setMicEnabled(value)
+setSpeakerEnabled(value)
}
DeviceHandler ..> GatewaySession : "返回JSON负载"
PermissionRequester --> MainActivity : "触发系统权限对话"
NodeRuntime --> PermissionRequester : "绑定相机/短信等权限"

网络通信与设备发现

  • WebSocket 会话:GatewaySession 建立 wss/ws 连接,发送 connect 挑战、签名设备信息、接收 canvasHostUrl 与会话默认值;支持 TLS 参数与指纹校验。
  • 自动连接与信任:NodeRuntime 在发现可信网关后自动连接,首次连接要求用户确认 TLS 指纹并持久化;手动连接模式要求已保存指纹。
  • 事件与 RPC:支持 node.event 推送与 node.invoke.request 调用,InvokeDispatcher 将请求路由到对应处理器并回传结果。
sequenceDiagram
participant RT as "NodeRuntime"
participant GS as "GatewaySession"
participant GW as "网关"
RT->>GS : connect(endpoint, token/password, options, tls)
GS->>GW : 建立WebSocket
GW-->>GS : challenge(nonce)
GS->>GS : 构造connect参数(签名/权限/能力)
GS->>GW : 发送connect
GW-->>GS : 返回server/canvasHostUrl/sessionDefaults
GS-->>RT : onConnected回调
RT->>GS : sendNodeEvent / request
GS-->>RT : 事件/响应

Android 权限体系与后台服务限制

  • 权限清单:应用声明 INTERNET、NETWORK_STATE、FOREGROUND_SERVICE、POST_NOTIFICATIONS、NEARBY_WIFI_DEVICES、LOCATION、CAMERA、RECORD_AUDIO、SMS、READ_MEDIA_*、READ_CONTACTS、READ_CALENDAR、ACTIVITY_RECOGNITION 等。
  • 动态权限:相机、录音、短信、通知监听等在运行时请求;PermissionRequester 提供理由对话与设置页引导。
  • 后台限制:应用通过前台服务维持数据同步;前台切换时停止语音会话以节省资源;最小化对电池与性能的影响。

依赖关系分析

  • 构建与工具链:根级 build.gradle.kts 引入 Android 插件、ktlint、Compose、Serialization 插件;app/build.gradle.kts 配置编译目标、签名、依赖与打包规则。
  • 第三方库:OkHttp、Material3、CameraX、dnsjava、BCProv、Commonmark、Kotlinx Serialization、Kotlinx Coroutines、Kotest/Robolectric 测试框架等。
  • 资源与图标:mipmap、values、xml 等资源目录用于主题、备份/数据提取规则、网络配置与文件提供者。
graph LR
A["根构建脚本"] --> B["应用模块构建脚本"]
B --> C["OkHttp/网络"]
B --> D["CameraX/相机"]
B --> E["Material3/Compose"]
B --> F["Kotlinx/序列化"]
B --> G["测试框架"]

性能考虑

  • 启动路径精简:MainActivity 在首帧后才启动前台服务,降低冷启动时间。
  • 拍摄与录制优化:拍照前旋转/缩放在主线程完成但耗时短;录屏使用最低质量与空预览激活编码器,缩短初始化时间。
  • 资源压缩:JPEG 压缩限制在 5MB 内,避免传输与解析开销过大。
  • 任务调度:使用 SupervisorJob 与 IO 线程池隔离网络与 I/O;状态流组合与去重减少 UI 重组。
  • 打包与混淆:开启资源压缩与 ProGuard 规则,排除无关文件与元数据。
昨天以前首页

万字解析 OpenClaw 源码架构-安全与权限

作者 毛骗导演
2026年3月9日 21:16

引言

本文件系统化阐述 OpenClaw 的安全模型与设计原则,覆盖整体安全架构、信任模型、威胁建模、风险评估、安全边界与攻击面、安全审计与修复、异常检测与入侵防护、安全配置基线、合规与度量指标,以及安全测试方法。目标读者为安全架构师与系统设计人员。

项目结构

OpenClaw 将安全能力贯穿于“网关认证与访问控制”“会话隔离”“工具执行沙箱”“外部内容处理”“安全审计与修复”等模块,并通过 CLI 提供统一的安全运维入口。下图展示与安全相关的核心目录与文件:

graph TB
subgraph "安全文档"
D1["docs/security/README.md"]
D2["docs/security/THREAT-MODEL-ATLAS.md"]
D3["docs/security/CONTRIBUTING-THREAT-MODEL.md"]
D4["docs/cli/security.md"]
end
subgraph "安全策略与政策"
P1["SECURITY.md"]
end
subgraph "运行时安全"
R1["src/gateway/auth.ts"]
R2["src/security/external-content.ts"]
R3["src/agents/sandbox/tool-policy.ts"]
end
subgraph "安全审计与修复"
A1["src/security/audit.ts"]
A2["src/security/fix.ts"]
end
D1 --> D2
D2 --> R1
D2 --> R2
D2 --> R3
D4 --> A1
A1 --> A2
P1 --> D1

核心组件

  • 网关认证与访问控制:负责身份鉴别、速率限制、代理信任与设备令牌登录(在受控界面场景启用)。
  • 外部内容处理:对外部输入进行标记、警告与清洗,防止提示注入与边界逃逸。
  • 工具策略与沙箱:基于允许/拒绝列表与组规则对工具调用进行强约束,保障最小权限与路径安全。
  • 安全审计与修复:提供可操作的配置检查、权限加固与自动修复建议,支持 JSON 输出以适配 CI/CD。

架构总览

OpenClaw 的安全架构以“个人助理”信任模型为核心,默认假设单个操作者(用户)可信,多用户共享同一网关实例不在推荐范围。系统通过以下信任边界与控制平面实现纵深防御:

  • 边界一:渠道接入(AllowFrom/白名单、配对期保护)
  • 边界二:会话隔离(会话键路由、工具策略)
  • 边界三:执行沙箱(容器或主机执行、命令审批)
  • 边界四:外部内容(抓取/邮件/Webhook 包装与警告)
  • 边界五:供应链(ClawHub 技能发布与审核)
graph TB
U["未受信区域<br/>渠道/外部源"] --> B1["边界一:渠道接入<br/>AllowFrom/配对保护"]
B1 --> B2["边界二:会话隔离<br/>会话键/工具策略"]
B2 --> B3["边界三:执行沙箱<br/>容器/主机+命令审批"]
B3 --> B4["边界四:外部内容<br/>包装/警告/模式检测"]
B4 --> B5["边界五:供应链<br/>ClawHub 审核/扫描"]

详细组件分析

组件一:网关认证与访问控制

  • 身份模式:支持 token、密码、受信代理转发与 Tailscale 设备令牌;默认优先使用 token 模式。
  • 速率限制:可按共享密钥作用域进行失败计数与锁定,缓解暴力破解。
  • 受信代理:严格校验代理地址、必要头字段与用户头,支持白名单用户。
  • 设备令牌登录:仅在受控界面(WS 控制 UI)场景启用,避免跨网络滥用。
  • 防护要点:禁止 loopback 绑定但无认证;严格 Origin 白名单;禁用危险的 Host 头回退;mDNS 全量模式泄露元数据。
sequenceDiagram
participant C as "客户端"
participant GW as "网关"
participant RL as "速率限制器"
participant TS as "Tailscale Whois"
C->>GW : 建立连接/握手
GW->>RL : 查询当前IP是否受限
RL-->>GW : 允许/拒绝并返回重试时间
alt 受信代理模式
GW->>GW : 校验代理地址与必要头
GW-->>C : 返回用户身份或拒绝
else 设备令牌登录
GW->>TS : 校验设备令牌来源与身份
TS-->>GW : 返回身份信息
GW-->>C : 认证成功
else 密码/token
GW-->>C : 要求凭据
C-->>GW : 提交凭据
GW->>RL : 记录失败/重置
GW-->>C : 认证结果
end

组件二:外部内容处理与提示注入防护

  • 标记与警告:对外部内容插入唯一边界标记与安全警告,明确不可作为系统指令。
  • 模式检测:识别可疑提示注入模式,用于监控与告警。
  • 标记清洗:对可能伪造的边界标记进行清洗,降低逃逸风险。
  • 包装策略:针对邮件、Webhook、Web 搜索/抓取等不同来源提供统一包装接口。
flowchart TD
Start(["接收外部内容"]) --> Detect["检测可疑模式"]
Detect --> Warn{"是否触发警告?"}
Warn --> |是| Log["记录可疑模式"]
Warn --> |否| Wrap["生成唯一ID并插入边界标记"]
Log --> Sanitize["清洗潜在边界标记"]
Wrap --> Sanitize
Sanitize --> Output["输出带警告与边界的包装内容"]

组件三:工具策略与沙箱

  • 策略解析:支持按代理与全局维度合并允许/拒绝列表,展开工具组,形成最终策略。
  • 默认策略:未显式允许时视为允许(空允许列表),但会注入必要的基础工具(如图像处理)以满足多模态工作流。
  • 沙箱边界:结合容器/主机执行与命令审批,确保工具调用在最小权限范围内执行。
flowchart TD
A["加载代理/全局工具策略"] --> B["展开工具组"]
B --> C["编译拒绝/允许正则"]
C --> D{"工具名匹配拒绝?"}
D --> |是| Deny["拒绝"]
D --> |否| E{"允许列表为空?"}
E --> |是| Allow["允许"]
E --> |否| F{"匹配允许?"}
F --> |是| Allow
F --> |否| Deny

组件四:安全审计与修复

  • 审计范围:文件系统权限、网关绑定与认证、受信代理、mDNS 模式、浏览器控制端点、插件与技能安全、Docker 沙箱设置、钩子与会话键覆盖、危险配置标志等。
  • 修复能力:自动收紧权限(chmod/ACL)、将通用组策略从开放改为白名单、调整敏感日志脱敏级别、写回安全配置变更。
  • 输出格式:支持人类可读报告与 JSON,便于 CI/CD 集成与自动化治理。
sequenceDiagram
participant CLI as "openclaw security"
participant AUD as "审计器"
participant FIX as "修复器"
participant FS as "文件系统/配置"
CLI->>AUD : 运行安全审计
AUD-->>CLI : 生成发现项与严重级别
CLI->>FIX : 请求修复 (--fix)
FIX->>FS : 修改权限/更新配置
FIX-->>CLI : 返回修复动作与结果

依赖关系分析

  • 文档层:安全文档与威胁模型指导实现层的设计与落地。
  • 实现层:认证模块依赖速率限制与代理信任配置;外部内容模块为所有外部输入提供统一包装;工具策略模块为执行层提供最小权限约束;审计与修复模块贯穿部署与运维生命周期。
  • 外部依赖:Node.js 版本安全补丁、Docker 安全运行参数(只读文件系统、丢弃多余能力)。
graph LR
TM["威胁模型<br/>THREAT-MODEL-ATLAS.md"] --> AUTH["认证模块<br/>auth.ts"]
TM --> EXT["外部内容<br/>external-content.ts"]
TM --> TOOL["工具策略<br/>tool-policy.ts"]
CLI["CLI 安全命令<br/>docs/cli/security.md"] --> AUD["审计器<br/>audit.ts"]
AUD --> FIX["修复器<br/>fix.ts"]
AUTH --> AUD
EXT --> AUD
TOOL --> AUD

性能考量

  • 审计与修复:采用增量检查与缓存(如代码安全性摘要缓存)减少重复开销;权限修复按需执行,避免不必要的文件系统写入。
  • 认证与速率限制:基于 IP 作用域的轻量级状态存储,避免阻塞式同步锁;Tailscale 设备令牌校验仅在受控界面启用。
  • 外部内容包装:边界标记与模式检测为常量时间复杂度;内容清洗按出现次数线性处理,整体开销可控。
  • 工具策略:策略编译与匹配采用预编译正则与通配符展开,匹配阶段为线性扫描,适合高频调用场景。

故障排查指南

  • 审计与修复
    • 使用 JSON 输出在 CI 中定位高危发现项,结合严重级别筛选关键问题。
    • 对于权限修复失败,检查符号链接、缺失文件与平台差异(Windows ACL)。
    • 对于配置写回失败,确认配置文件存在性与解析快照有效性。
  • 认证与访问控制
    • 若 loopback 绑定但无认证导致访问异常,检查网关 token 或密码配置。
    • 受信代理模式下,确认代理地址、必要头与用户头配置正确。
    • 浏览器控制端点无认证时,启用网关认证并重启以自动生成 token。
  • 外部内容
    • 若提示注入告警频繁,检查外部来源是否包含边界标记或可疑模式文本。
    • 包装后内容仍被误判,可在调用侧关闭详细警告以减少噪声。
  • 工具策略
    • 工具调用被拒绝时,检查代理/全局允许/拒绝列表与工具组展开结果。
    • 图像处理等基础工具未生效,确认未被显式拒绝且允许列表非空。

安全设计原则

  • 最小权限:工具与会话默认拒绝,除非显式允许。
  • 信任模型:单操作者可信,多用户共享不推荐;严格边界隔离。
  • 输入不可信:外部内容一律包装与警告,提示注入检测仅作监控。
  • 可观测与可修复:审计与修复自动化,JSON 输出便于集成。

威胁缓解策略

  • 直接/间接提示注入:包装与警告 + 模式检测 + 命令审批。
  • 供应链攻击:ClawHub 审核与扫描(逐步完善)。
  • 执行绕过:工具策略 + 沙箱 + 命令审批。
  • 外部内容逃逸:多层边界标记 + 清洗 + 安全警告。

安全配置基线

  • 网关
    • 绑定:优先 loopback;若非 loopback,必须配置认证与速率限制。
    • 受信代理:严格配置代理地址、必要头与用户白名单。
    • mDNS:优先 minimal/off,避免元数据泄露。
    • 控制界面:Origin 白名单必填;禁用危险 Host 头回退。
  • 文件系统
    • 状态目录与配置文件权限收紧至 700/600;凭证目录与会话文件同权限。
    • 插件/钩子安装记录应固定版本与完整性校验。
  • 沙箱
    • Docker:只读文件系统、丢弃多余能力;避免 host/容器命名空间共享。
    • 浏览器沙箱:桥接网络需限定来源范围;容器标签一致性校验。
  • 外部内容
    • 启用敏感日志脱敏;对 web_fetch 与外部来源实施 URL 白名单与内容包装。

合规与度量指标

  • 合规要求
    • Node.js 版本满足安全补丁要求。
    • Docker 运行参数符合最小暴露面原则。
    • 审计与修复流程纳入 CI/CD。
  • 度量指标
    • 审计发现数量与严重级别分布(critical/warn/info)。
    • 权限修复成功率与失败原因统计。
    • 提示注入告警频率与模式检测命中率。
    • 工具调用拒绝率与误报率。

安全审计

安全审计体系由“审计引擎 + 报告输出 + 日志与可观测性”三部分构成:

  • 审计引擎:集中于 src/security 下,按同步/异步职责拆分,统一汇聚为报告对象。
  • 报告输出:CLI 命令 status 中集成安全审计结果展示;支持 JSON 输出用于自动化集成。
  • 日志与可观测性:文档化日志位置、格式、级别与导出;提供诊断事件目录与 OTLP 导出能力。
graph TB
subgraph "审计引擎"
A["audit.ts<br/>主审计器"]
B["audit-extra.sync.ts<br/>同步审计收集器"]
C["audit-extra.async.ts<br/>异步审计收集器"]
end
subgraph "报告与CLI"
D["status.command.ts<br/>状态命令集成审计"]
end
subgraph "日志与可观测性"
E["docs/logging.md<br/>日志与导出指南"]
F["src/logging.ts<br/>日志模块入口"]
end
A --> B
A --> C
D --> A
D --> E
E --> F

核心组件

  • 审计主控制器
    • 统一选项与上下文构建,汇总各类审计收集器结果,生成带严重性统计的报告对象。
    • 关键类型:SecurityAuditOptions、SecurityAuditReport、SecurityAuditFinding。
  • 同步审计收集器
    • 基于配置的静态分析,不涉及 I/O,覆盖攻击面概览、钩子加固、网关 HTTP 无认证、沙箱策略、插件安装与完整性、模型与工具策略等。
  • 异步审计收集器
    • 执行文件系统与外部状态检查,如状态目录与配置文件权限、日志文件可读性、扩展插件安装版本漂移、容器端口发布范围、浏览器沙箱标签一致性等。
  • 报告与 CLI 集成
    • status 命令运行审计并以人类可读方式输出摘要与修复建议;支持 JSON 输出便于自动化处理。
  • 日志与可观测性
    • 文档化文件日志位置、级别、控制台样式、敏感信息脱敏策略;提供诊断事件目录与 OTLP 导出配置。

架构总览

下图展示从 CLI 到审计引擎再到日志系统的调用链路与数据流。

sequenceDiagram
participant CLI as "CLI/用户"
participant Status as "status.command.ts"
participant Audit as "audit.ts 主审计器"
participant Sync as "audit-extra.sync.ts"
participant Async as "audit-extra.async.ts"
participant Logs as "docs/logging.md + src/logging.ts"
CLI->>Status : 执行 openclaw status
Status->>Audit : runSecurityAudit(options)
Audit->>Sync : 调用同步收集器
Audit->>Async : 调用异步收集器
Audit-->>Status : 返回 SecurityAuditReport
Status->>Status : 格式化摘要与修复建议
Status-->>CLI : 输出人类可读/JSON 报告
Status->>Logs : 参考日志配置与导出

详细组件分析

审计主控制器(audit.ts)

  • 职责
    • 解析与标准化审计选项,构建执行上下文。
    • 调用同步/异步收集器,合并结果,统计严重性。
    • 可选深探针(deep),对网关可达性与握手进行探测。
  • 关键点
    • 通过选项注入测试依赖(execIcacls、execDockerRawFn、probeGatewayFn 等)。
    • 支持预加载配置快照以减少审计时文件读取。
    • 严格区分“文件系统检查”“通道安全检查”“深探针”等维度。
flowchart TD
Start(["进入 runSecurityAudit"]) --> Ctx["构建执行上下文<br/>解析选项/环境/平台"]
Ctx --> Collect["调用同步/异步收集器"]
Collect --> Merge["合并发现项并统计严重性"]
Merge --> Deep{"是否启用 deep 探针?"}
Deep --> |是| Probe["探测网关可达性/握手"]
Deep --> |否| Report["生成报告"]
Probe --> Report
Report --> End(["返回 SecurityAuditReport"])

同步审计收集器(audit-extra.sync.ts)

  • 攻击面概览与策略
    • 汇总各通道组策略、工具提升权限、钩子开关、浏览器控制等,形成“攻击面摘要”。
  • 钩子加固
    • 检查 hooks.token 是否过短、是否与网关令牌重复、默认会话键是否配置、请求级会话键前缀限制等。
  • 网关 HTTP 无认证
    • 当 auth.mode 为 none 且存在启用的 HTTP 端点时,给出严重性提示。
  • 沙箱与容器
    • 检查 agents.defaults.sandbox.docker 配置与模式不一致、容器端口发布范围、标签哈希一致性等。
  • 插件与安装
    • 检查 plugins.allow 是否缺失、npm 安装规范是否固定、完整性元数据是否存在、版本漂移等。
  • 工具策略与模型
    • 分析工具策略、模型选择与回退、小模型风险、最小化配置覆盖等。

异步审计收集器(audit-extra.async.ts)

  • 文件系统与状态
    • 检查 stateDir、configPath、auth-profiles.json、sessions.json、日志文件等路径权限与可读性。
  • 浏览器沙箱容器
    • 列举并检查沙箱浏览器容器的 configHash 标签、epoch 标签、端口发布范围。
  • 插件与安装(续)
    • 读取已安装插件目录、解析允许列表、校验 npm 规范与完整性、版本漂移。
  • 代码安全扫描摘要缓存
    • 对工作区技能文件扫描结果进行缓存,降低重复审计成本。

报告与 CLI 集成(status.command.ts)

  • 在 status 命令中运行审计,输出摘要与修复建议;支持 JSON 输出。
  • 对“重要发现”(critical/warn)进行排序与截断显示,便于快速定位。

日志与可观测性(docs/logging.md + src/logging.ts)

  • 日志位置与格式
    • 默认滚动文件日志(JSON Lines),CLI 与 Control UI 实时尾随。
    • 控制台输出支持 pretty/compact/json 三种风格,受 logging.consoleStyle 控制。
  • 敏感信息脱敏
    • logging.redactSensitive 控制控制台输出脱敏级别;不影响文件日志。
  • 诊断事件与 OTLP 导出
    • 提供模型使用、消息流转、队列与会话等诊断事件目录。
    • 通过 diagnostics-otel 插件导出至任意 OTLP/HTTP 收集器,支持采样与刷新间隔配置。

安全策略与合规要求(SECURITY.md)

  • 报告流程与接受标准
    • 明确漏洞报告所需要素、快速通道要求、常见误报场景与范围界定。
  • 运营商信任模型
    • 强调“个人助理”信任边界,明确会话键不是多租户授权边界。
  • 远程暴露与加固
    • 网关控制 UI 应本地使用;非本地部署需强认证与防火墙/隧道控制。
  • 运行时要求与 Docker 安全
    • Node.js 版本安全基线;Docker 运行建议(只读、丢弃能力等)。

审计策略配置与最佳实践

  • 审计范围
    • includeFilesystem/includeChannelSecurity/deep 等选项决定审计粒度与深度。
  • 修复建议
    • 使用 openclaw security audit --fix 自动收紧权限与策略(如 groupPolicy、logging.redactSensitive、文件权限)。
  • 通道安全
    • 文档化常见通道安全问题与缓解措施,结合审计结果进行优先级排序。

依赖关系分析

  • 组件耦合
    • audit.ts 作为编排器,依赖同步/异步收集器与文件系统/网络探测能力。
    • status.command.ts 仅负责调用与展示,不直接参与审计逻辑,保持低耦合。
  • 外部依赖
    • 日志系统通过 docs/logging.md 与 src/logging.ts 提供统一入口。
    • OTLP 导出依赖 diagnostics-otel 插件与第三方收集器。
graph LR
Status["status.command.ts"] --> Audit["audit.ts"]
Audit --> Sync["audit-extra.sync.ts"]
Audit --> Async["audit-extra.async.ts"]
Status --> Docs["docs/logging.md"]
Docs --> Impl["src/logging.ts"]

性能考量

  • 缓存与去重
    • 异步收集器对代码安全扫描摘要使用 Map 缓存,避免重复计算。
  • I/O 优化
    • 通过 configSnapshot 预加载配置,减少审计时文件读取。
  • 深探针超时
    • deepTimeoutMs 可控,默认约 5 秒,避免阻塞 CLI。
  • 日志级别与导出
    • 通过 logging.level 与 diagnostics.otel.sampleRate 控制日志体积与导出压力。

故障排查指南

  • 审计未发现问题但怀疑配置不当
    • 使用 --deep 启用深探针,检查网关可达性与握手;关注 critical/warn 发现项。
  • 日志为空或不可达
    • 使用 openclaw doctor 排查;确认 logging.file 指向正确路径;必要时提高 logging.level。
  • 控制 UI 或远程访问异常
    • 结合 SECURITY.md 的远程暴露与加固建议,确保 gateway.bind 与 auth 配置符合信任模型。
  • 插件/钩子安全风险
    • 检查 plugins.allow、npm 规范与完整性、hooks.token 独立性与长度;避免与网关令牌重复。

审计数据保护与隐私考虑

  • 敏感信息脱敏
    • 控制台输出可通过 logging.redactSensitive 脱敏;文件日志默认保留原始内容。
  • 日志导出与 OTLP
    • OTLP 导出遵循 file log 级别,控制台脱敏不适用于导出日志。
  • 通道与会话边界
    • 会话键仅路由控制,非多租户授权边界;跨用户隔离需通过 OS 用户/主机/网关分离实现。

安全与权限

围绕“安全与权限”的关键目录与文件:

  • 文档层:安全策略、威胁模型、CLI 安全审计参考
  • 核心实现层:网关认证、外部内容包装、安全审计与修复、危险配置检测
graph TB
A["安全策略<br/>SECURITY.md"] --> B["威胁模型<br/>docs/security/THREAT-MODEL-ATLAS.md"]
B --> C["CLI 安全审计参考<br/>docs/cli/security.md"]
D["安全审计实现<br/>src/security/audit.ts"] --> E["安全修复实现<br/>src/security/fix.ts"]
F["危险配置检测<br/>src/security/dangerous-config-flags.ts"] --> D
G["外部内容包装<br/>src/security/external-content.ts"] --> D
H["网关认证<br/>src/gateway/auth.ts"] --> D

核心组件

  • 信任模型与边界
    • 个人助理模型(单受信任操作者)与多用户共享边界不被当作多租户隔离边界;会话标识为路由控制而非授权边界。
    • 插件作为可信计算基的一部分,安装即授予与本地主机同等的信任级别。
  • 认证与授权
    • 网关支持令牌/密码/受信代理模式;可选 Tailscale 身份验证;HTTP 工具调用默认对危险工具进行限制。
  • 外部内容处理
    • 统一封装与安全提示,避免将不受信内容直接作为系统指令或命令。
  • 沙箱与工具执行
    • 默认以主机优先(可配置),工具策略与执行审批共同构成第二道防线;建议在多风险场景启用沙箱。
  • 安全审计与修复
    • CLI 提供安全审计与可选修复能力,覆盖文件权限、暴露面、危险标志、浏览器控制端点等。

架构总览

下图展示从通道到会话、工具执行、外部内容与供应链的跨边界信任与保护:

graph TB
U["未信任区域<br/>消息通道/外部服务"] --> GW["网关<br/>认证/路由"]
GW --> SESS["会话隔离<br/>会话键/工具策略"]
SESS --> TOOL["工具执行<br/>策略/审批/沙箱"]
TOOL --> EXT["外部内容<br/>包装/警告/标记"]
TOOL --> SUPPLY["供应链<br/>ClawHub/技能发布"]

详细组件分析

组件一:安全审计与修复(CLI)

  • 功能要点
    • 审计:检查配置与状态目录权限、暴露面、危险标志、浏览器控制端点、钩子与工具策略、mDNS 元数据泄露、Docker 沙箱网络等。
    • 修复:自动收紧权限(状态目录、配置、凭据、会话日志)、将部分组策略从开放改为白名单、调整敏感信息脱敏级别。
  • 关键行为
    • 对非 loopback 绑定且无认证的网关发出严重告警。
    • 对允许在 HTTP 上重新启用危险工具的行为发出严重告警。
    • 对危险标志(如禁用设备认证、Host Header 来源回退)进行汇总与提醒。
  • 输出格式
    • 支持 JSON 输出,便于 CI/策略检查;结合 --fix 可输出修复动作与最终报告。
flowchart TD
Start(["开始审计"]) --> FS["检查文件系统权限"]
FS --> GW["检查网关配置与暴露面"]
GW --> BR["检查浏览器控制端点"]
BR --> HK["检查钩子与工具策略"]
HK --> DK["检查 Docker 沙箱与 mDNS"]
DK --> Flags["收集危险配置标志"]
Flags --> Report["生成报告/JSON"]
Report --> Fix{"是否启用 --fix?"}
Fix --> |是| Apply["应用修复动作"]
Fix --> |否| End(["结束"])
Apply --> End

组件二:网关认证与授权

  • 模式与解析
    • 支持模式:无、令牌、密码、受信代理;默认令牌模式;可按环境变量与配置解析凭据。
    • 受信代理模式要求明确的用户头、允许用户列表与代理地址范围。
    • Tailscale 身份验证仅在特定场景启用,且需通过反向代理转发的头部校验。
  • 授权流程
    • 首先判定受信代理/无认证/令牌/密码路径;失败时记录速率限制;成功后重置速率限制。
    • 对于非本地直连请求,可启用 Tailscale 身份校验。
  • 安全注意
    • 非 loopback 绑定且无认证将被标记为高危;速率限制建议开启以抵御暴力破解。
sequenceDiagram
participant C as "客户端"
participant GW as "网关"
participant RL as "速率限制器"
participant TS as "Tailscale Whois"
C->>GW : 建立连接/握手
GW->>RL : 检查速率限制
alt 受信代理模式
GW->>GW : 校验代理与用户头
GW-->>C : 授权结果
else 令牌/密码模式
GW->>GW : 校验凭据
GW-->>C : 授权结果
else Tailscale 身份
GW->>TS : 查询身份
TS-->>GW : 返回身份
GW-->>C : 授权结果
end
RL-->>GW : 成功则重置,失败则记录

组件三:外部内容处理与提示

  • 设计原则
    • 不将外部内容视为可信指令;统一使用随机 ID 的边界标记与安全提示文本包裹。
    • 对可能的注入模式进行检测与记录;对已知边界标记进行清洗,防止伪造。
  • 使用场景
    • 邮件、Webhook、Web 搜索/抓取等外部输入在进入 LLM 前必须经过封装与上下文提示。
  • 会话来源识别
    • 通过会话键前缀识别外部钩子来源(如 gmail/webhook),并据此决定是否包含更严格的警告。
flowchart TD
In(["接收外部内容"]) --> San["清洗边界标记"]
San --> Warn["附加安全提示与元数据"]
Warn --> Mark["生成唯一边界标记"]
Mark --> Out(["返回安全提示后的封装内容"])

组件四:威胁模型与关键风险

  • MITRE ATLAS 框架下的威胁分类与风险矩阵
    • 关键威胁:直接/间接提示注入、工具参数注入、执行审批绕过、恶意技能发布、供应链更新投毒、配置篡改、内容包装逃逸、资源耗尽攻击等。
    • 关键攻击链:技能持久化→规避审核→凭据窃取;提示注入→审批绕过→远程命令执行;间接注入→外发数据。
  • 建议与优先级
    • 立即(P0):完善病毒扫描、技能沙箱、敏感动作输出校验;
    • 短期(P1):速率限制、凭据加密、改进审批 UX 与 URL 白名单;
    • 中期(P2):通道加密验证、配置完整性校验、更新签名与版本固定。

依赖关系分析

  • 审计模块依赖
    • 配置解析与路径解析、网关认证解析、Docker 执行、浏览器配置解析、通道插件清单、Windows ACL 工具等。
  • 危险配置检测
    • 从配置中扫描危险标志位,用于审计报告与修复建议。
  • 外部内容模块
    • 与审计模块配合,确保外部输入在进入系统前被安全封装。
graph LR
Audit["audit.ts"] --> Auth["gateway/auth.ts"]
Audit --> Fix["fix.ts"]
Audit --> Flags["dangerous-config-flags.ts"]
Audit --> Ext["external-content.ts"]
Audit --> Paths["配置/路径解析"]
Audit --> Docker["Docker 执行"]
Audit --> Browser["浏览器配置"]
Audit --> Plugins["通道插件"]

性能考量

  • 审计与修复
    • 文件系统权限检查、Docker 标签检查、Windows ACL 重置等操作可能涉及磁盘与进程调用,建议在 CI/自动化中合理缓存与批量化执行。
  • 认证与授权
    • 速率限制器应避免对缺失凭据的请求计为失败尝试,减少误判与资源消耗。
  • 外部内容处理
    • 标记清洗与正则匹配成本可控,但应避免对超大文本重复执行;可在上游做分块或采样。

凭证管理

凭证管理相关能力主要分布在以下模块:

  • 配置与类型:定义SecretRef契约、默认提供者、解析与校验逻辑
  • 网关凭据解析:统一处理本地/远程模式、环境变量优先级、显式覆盖与错误提示
  • 凭据矩阵与表面:声明支持/不支持的凭据路径,指导配置与审计
  • 文档与CLI:提供Secrets管理指南、命令参考与审计流程
  • 认证语义:对令牌有效期、未解析引用等进行稳定语义定义
graph TB
subgraph "配置与类型"
T1["src/config/types.secrets.ts<br/>SecretRef契约/解析/默认提供者"]
M1["src/secrets/credential-matrix.ts<br/>凭据矩阵/排除项"]
end
subgraph "网关凭据解析"
C1["src/gateway/credentials.ts<br/>凭据来源/优先级/错误模型"]
P1["src/gateway/credential-precedence.parity.test.ts<br/>用例验证优先级一致性"]
end
subgraph "文档与CLI"
D1["docs/gateway/secrets.md<br/>Secrets管理指南"]
CLI["docs/cli/secrets.md<br/>CLI命令参考"]
SURF["docs/reference/secretref-credential-surface.md<br/>凭据表面清单"]
SEM["docs/auth-credential-semantics.md<br/>认证语义"]
end
subgraph "认证状态"
CS["src/agents/auth-profiles/credential-state.ts<br/>令牌有效期/可解析性评估"]
end
T1 --> C1
M1 --> D1
SURF --> D1
D1 --> CLI
C1 --> P1
CS --> D1
SEM --> CS

核心组件

  • SecretRef契约与解析
    • 定义三类来源:环境变量(env)、文件(file)、执行(exec),并提供默认提供者别名与校验规则
    • 支持从字符串模板或对象形式解析SecretRef,并在配置阶段进行合法性检查
  • 运行时凭据解析与优先级
    • 统一处理本地/远程网关模式、显式覆盖、环境变量与配置文件优先级
    • 对不可解析的SecretRef抛出明确错误,避免“占位符”被误认为有效值
  • 凭据矩阵与表面
    • 明确列出支持与不支持的凭据路径,指导配置与审计;排除运行时生成/轮换/会话类凭据
  • CLI与审计工作流
    • 提供reload、audit、configure、apply四大命令,配合计划文件实现可审计的一次性变更
  • 认证语义与有效期
    • 对令牌型凭据定义稳定的reason code(如缺失、过期、无效)与解析规则

架构总览

系统采用“只读外部SecretRef解析 + 运行时快照”的设计,确保:

  • 解析在激活阶段一次性完成,热路径只读内存快照
  • 启动/重载失败快速中止,避免部分激活导致的不一致
  • 原子替换快照,失败保留上一次已知良好状态
  • 仅对“有效表面”进行严格校验,非活跃面允许降级诊断
sequenceDiagram
participant Operator as "操作者"
participant CLI as "CLI(secrets)"
participant Gateway as "网关RPC"
participant Resolver as "Secret解析器"
participant Providers as "提供者(env/file/exec)"
Operator->>CLI : 执行 "secrets reload/audit/configure/apply"
CLI->>Gateway : 调用 secrets.reload 或触发重载
Gateway->>Resolver : 解析所有SecretRef(激活前)
Resolver->>Providers : 并发拉取(受并发/批次限制)
Providers-->>Resolver : 返回键值映射
Resolver-->>Gateway : 返回完整快照或错误
alt 成功
Gateway->>Gateway : 原子交换快照
Gateway-->>CLI : 返回成功(含警告计数)
else 失败
Gateway->>Gateway : 保持上次已知良好快照
Gateway-->>CLI : 返回错误(不含部分激活)
end

组件详解

SecretRef契约与解析

  • 结构与默认提供者
    • SecretRef由三元组(source/provider/id)组成,支持env/file/exec三种来源
    • 默认提供者别名用于兼容旧格式与简化配置
  • 字符串模板与对象解析
    • 支持从形如"${ENV_VAR}"的模板推导SecretRef
    • 对象形式要求字段齐全且满足正则约束
  • 解析与断言
    • 在读取前断言SecretRef已解析;未解析时抛出带路径信息的错误
    • 对空字符串与残留占位符进行过滤,避免误判为有效值
flowchart TD
Start(["开始"]) --> Parse["解析输入(字符串/对象/模板)"]
Parse --> Coerce{"是否为合法SecretRef?"}
Coerce --> |否| Normalize["标准化字符串(去空白)"]
Normalize --> HasValue{"是否有非空值?"}
HasValue --> |否| AssertRef["断言: 必须已解析"]
HasValue --> |是| Done(["返回值"])
Coerce --> |是| Done

网关凭据解析与优先级

  • 来源与优先级
    • 显式参数优先于配置与环境变量
    • 本地/远程模式下,token与password的优先顺序不同
    • 服务端网关运行时对token采用“配置优先”,其他路径采用“环境优先”
  • 错误模型
    • 当SecretRef在当前命令路径不可用时,抛出明确错误并给出修复建议
    • 对残留占位符进行拒绝,防止“看起来像值”的文本被接受
  • 兼容性
    • 保留对历史环境变量的支持,但探测/状态等路径忽略历史变量
sequenceDiagram
participant Cmd as "命令路径"
participant Cfg as "配置/默认"
participant Env as "环境变量"
participant Err as "错误模型"
Cmd->>Cfg : 读取显式参数/配置
Cmd->>Env : 读取OPENCLAW_*与历史变量
Cmd->>Cmd : 按优先级选择(token/password)
alt 任一SecretRef未解析
Cmd->>Err : 抛出不可用错误(含修复指引)
else 全部可用
Cmd-->>Cmd : 返回最终凭据
end

凭据矩阵与表面

  • 支持范围
    • 明确列出可在openclaw.json与auth-profiles.json中使用SecretRef的目标路径
    • 包括模型提供商apiKey、工具搜索key、网关认证与远端凭据、各渠道令牌/密钥等
  • 不支持范围
    • 排除运行时生成/轮换/会话类凭据(OAuth刷新材料、access token、会话密钥等)
  • Google Chat兼容例外
    • 允许通过兄弟字段serviceAccountRef覆盖serviceAccount
classDiagram
class MatrixEntry {
+id : string
+configFile : "openclaw.json"|"auth-profiles.json"
+path : string
+refPath? : string
+when? : {type}
+secretShape : "secret_input"|"sibling_ref"
+optIn : true
+notes? : string
}
class Excluded {
+列表 : 排除项数组
}
MatrixEntry --> Excluded : "受排除规则约束"

CLI与审计工作流

  • 命令角色
    • reload:重新解析并原子交换快照
    • audit:扫描明文残留、未解析引用、优先级遮蔽与遗留数据
    • configure:交互式构建提供者与目标映射,预演后应用
    • apply:执行保存的计划,一次性清理明文残留
  • 退出码与报告
    • audit --check在发现问题时返回非零;未解析引用有更高优先级
    • apply不写回滚备份,采用原子替换与尽力恢复策略
flowchart TD
A["开始"] --> B["secrets audit --check"]
B --> C{"发现明文/未解析/遮蔽/遗留?"}
C --> |是| D["secrets configure"]
C --> |否| E["secrets reload"]
D --> F["secrets apply --dry-run"]
F --> G{"确认执行?"}
G --> |是| H["secrets apply(无回滚备份)"]
G --> |否| I["终止"]
H --> J["secrets audit --check 再验证"]
J --> K["结束"]

认证语义与有效期

  • 稳定reason code
    • ok、missing_credential、invalid_expires、expired、unresolved_ref
  • 令牌有效期规则
    • expires必须为有限正数;无效或已过期的令牌视为不可用
    • tokenRef不会绕过expires校验
  • 兼容性消息
    • 为脚本兼容保留首行不变的消息,后续追加人类可读细节与稳定reason code
flowchart TD
S(["开始"]) --> T["判断类型(api_key/token/oauth)"]
T --> |api_key| K{"是否存在key或keyRef?"}
K --> |否| R1["reason=missing_credential"]
K --> |是| OK["eligible=true"]
T --> |token| TK{"是否存在token或tokenRef?"}
TK --> |否| R2["reason=missing_credential"]
TK --> |是| EXP["校验expires(有限正数)"]
EXP --> |无效| R3["reason=invalid_expires"]
EXP --> |已过期| R4["reason=expired"]
EXP --> |有效| OK
T --> |oauth| ACC{"是否存在access或refresh?"}
ACC --> |否| R5["reason=missing_credential"]
ACC --> |是| OK

依赖关系分析

  • 类型层依赖
    • 网关凭据解析依赖SecretRef契约与默认提供者配置
    • 凭据矩阵为文档与CLI提供“支持/不支持”清单
  • 行为一致性
    • 测试用例覆盖调用/探测/状态/认证四条路径的优先级一致性
  • 运行时契约
    • CLI命令与网关RPC共同保证“预检通过再提交、失败保留上次快照”
graph LR
Types["types.secrets.ts"] --> Creds["gateway/credentials.ts"]
Matrix["secrets/credential-matrix.ts"] --> Docs["gateway/secrets.md"]
Docs --> CLI["cli/secrets.md"]
Creds --> Test["gateway/credential-precedence.parity.test.ts"]
Sem["auth-credential-semantics.md"] --> State["agents/auth-profiles/credential-state.ts"]

性能考量

  • 并发与批处理
    • 解析器支持最大提供者并发、每提供者最大引用数与批量字节上限,避免资源耗尽
  • 路径安全
    • 文件提供者与执行提供者对路径所有权/权限进行严格校验;Windows与包管理器路径需显式信任
  • 启动与重载
    • 激活阶段一次性解析,热路径只读内存快照,减少IO与解析开销

认证与授权

OpenClaw 在不同层面实现了认证与授权能力:

  • 设备侧认证:设备令牌、角色与作用域、元数据规范化与签名载荷构建
  • 网关侧认证:WebSocket 连接鉴权、速率限制、方法级授权
  • 渠道侧授权:直接消息(DM)与群组的访问控制、命令授权门禁
  • 凭据与令牌:OAuth 令牌交换与存储、API 密钥解析与轮换
  • 会话与审计:会话存储、缓存与维护、会话键映射与重置
graph TB
subgraph "设备侧"
DA["设备认证载荷构建<br/>device-auth.ts"]
DS["设备认证数据结构<br/>shared/device-auth.ts"]
end
subgraph "网关侧"
RP["角色策略<br/>role-policy.ts"]
AC["WS 鉴权上下文<br/>ws-connection/auth-context.ts"]
end
subgraph "渠道侧"
DM["DM/群组访问策略<br/>dm-policy-shared.ts"]
CMD["命令授权门禁<br/>plugin-sdk/command-auth.ts"]
end
subgraph "凭据与令牌"
OA["OAuth 与令牌存储<br/>concepts/oauth.md + authentication.md"]
MA["模型认证解析<br/>agents/model-auth.ts"]
OH["OAuth 实现细节<br/>agents/auth-profiles/oauth.ts"]
end
subgraph "会话与审计"
SS["会话存储与缓存<br/>config/sessions/store.ts"]
SM["会话键映射<br/>acp/session-mapper.ts"]
ST["会话工具解析<br/>agents/tools/session-status-tool.ts"]
end
DA --> AC
DS --> AC
RP --> AC
DM --> CMD
CMD --> AC
OA --> MA
OH --> MA
SS --> SM
SS --> ST

核心组件

  • 设备认证与令牌管理
    • 设备认证载荷构建与版本化(v2/v3),包含设备 ID、客户端标识、角色、作用域、时间戳、可选令牌与随机数,并对平台与设备家族字段进行规范化
    • 设备认证条目与本地存储结构,支持角色与作用域的标准化与去重排序
  • 角色与方法授权
    • 网关角色集合与解析,角色是否可跳过设备身份校验的判定,以及基于方法的授权规则(节点角色仅允许节点相关方法,操作员角色仅允许管理相关方法)
  • 渠道访问控制与命令授权
    • DM/群组访问策略:根据策略类型(禁用/开放/需要配对/白名单)与允许列表计算有效允许列表,决定放行、需要配对或阻断
    • 命令授权门禁:结合“是否启用访问组”“文本命令”“是否存在控制命令”等参数,评估命令授权结果
  • 凭据与令牌管理
    • OAuth 令牌交换与存储:集中式凭据存储、刷新与过期处理、多账户(配置文件)路由
    • API 密钥解析与轮换:环境变量优先级、速率限制下的备用密钥重试
  • 会话管理与审计
    • 会话存储:原子写入、缓存(TTL)、磁盘预算、归档与清理、锁队列与并发控制
    • 会话键映射与重置:标签/键解析、按需重置会话

架构总览

下图展示从设备到网关、再到渠道与会话的整体认证与授权流程。

sequenceDiagram
participant Dev as "设备"
participant GW as "网关"
participant Role as "角色策略"
participant Rate as "速率限制"
participant Auth as "设备令牌验证"
participant DM as "DM/群组策略"
participant Cmd as "命令授权门禁"
participant Sess as "会话存储"
Dev->>GW : "建立连接并携带设备令牌"
GW->>Rate : "检查设备速率限制"
Rate-->>GW : "允许/拒绝"
GW->>Auth : "验证设备令牌与角色/作用域"
Auth-->>GW : "通过/失败"
GW->>Role : "方法级授权判定"
Role-->>GW : "允许/拒绝"
GW->>DM : "评估 DM/群组访问策略"
DM-->>GW : "放行/需要配对/阻断"
GW->>Cmd : "命令授权门禁评估"
Cmd-->>GW : "授权/阻断"
GW->>Sess : "记录会话元数据/更新"
Sess-->>GW : "持久化完成"
GW-->>Dev : "建立会话并返回状态"

详细组件分析

设备认证与令牌管理

  • 载荷构建
    • v2:设备 ID、客户端 ID、客户端模式、角色、作用域列表、签名时间戳、令牌、随机数
    • v3:在 v2 基础上增加平台与设备家族字段的规范化
  • 本地存储
    • 条目包含令牌、角色、作用域与更新时间;支持角色与作用域的标准化(去空白、去重、排序)
  • 平台与设备家族规范化
    • 统一为 ASCII 小写形式,确保跨平台一致性
  • 测试与兼容
    • 单测覆盖载荷向量与元数据规范化;Android 侧同样实现一致逻辑
flowchart TD
Start(["开始"]) --> BuildV2["构建 v2 载荷<br/>设备ID|客户端ID|模式|角色|作用域|时间戳|令牌|随机数"]
BuildV2 --> BuildV3["构建 v3 载荷<br/>在 v2 基础上附加平台与设备家族"]
BuildV3 --> Normalize["规范化平台/设备家族为 ASCII 小写"]
Normalize --> Store["本地存储条目<br/>令牌+角色+作用域+更新时间"]
Store --> End(["结束"])

角色权限模型与方法授权

  • 角色集合与解析:支持 operator 与 node
  • 设备身份豁免:仅当共享认证可用时,operator 可跳过设备身份
  • 方法授权:节点方法仅允许 node 角色,管理方法仅允许 operator 角色
classDiagram
class 角色策略 {
+解析角色(raw)
+可跳过设备身份(role, sharedAuthOk)
+按方法授权(role, method)
}
角色策略 <|.. 网关角色 : "operator/node"

渠道访问控制与命令授权

  • DM/群组访问策略
    • 决策依据:策略类型(禁用/开放/需要配对/白名单)、允许列表、存储中的允许列表
    • 结果:放行、需要配对、阻断,并附带原因码
  • 命令授权门禁
    • 结合“是否启用访问组”“文本命令”“是否存在控制命令”,评估命令授权与是否阻断控制命令
flowchart TD
A["输入: 是否群组/策略类型/允许列表/发送者"] --> B["计算有效允许列表"]
B --> C{"策略类型"}
C --> |禁用| D["阻断"]
C --> |开放| E["放行"]
C --> |需要配对| F{"是否在允许列表?"}
F --> |是| E
F --> |否| G["需要配对"]
C --> |白名单| F
E --> H["命令授权门禁评估"]
G --> H
D --> H
H --> I["授权/阻断"]

凭据与令牌管理(OAuth 与 API 密钥)

  • OAuth
    • 令牌交换(PKCE)、集中式凭据存储(auth-profiles.json)、刷新与过期处理、多账户(配置文件)路由
    • 支持提供商插件自带 OAuth 或 API 密钥流程
  • API 密钥
    • 解析顺序:单值覆盖、环境变量列表、备用键、Google 额外回退键;仅在速率限制错误时重试下一个密钥
sequenceDiagram
participant User as "用户/CLI"
participant Wizard as "向导/命令"
participant OAuth as "OAuth 实现"
participant Store as "凭据存储"
participant Provider as "模型提供方"
User->>Wizard : "选择 OAuth/设置令牌"
Wizard->>OAuth : "发起 PKCE 登录/粘贴令牌"
OAuth->>Provider : "交换令牌/获取账户信息"
Provider-->>OAuth : "返回访问/刷新令牌与过期时间"
OAuth->>Store : "写入/更新凭据"
Store-->>Wizard : "确认状态"
Wizard-->>User : "完成"

会话管理与审计

  • 会话存储
    • 原子写入、缓存(TTL)、磁盘预算、归档与清理、锁队列与并发控制
    • 键规范化、迁移、维护(修剪、封顶、轮转)
  • 会话键映射与重置
    • 支持标签/键解析、存在性校验、按需重置
flowchart TD
L["加载会话存储"] --> N["规范化条目/键"]
N --> M{"维护模式"}
M --> |告警| W["仅警告不强制执行"]
M --> |执行| P["修剪过期/封顶数量/轮转文件/磁盘预算"]
P --> A["归档/清理历史会话"]
W --> R["报告维护统计"]
A --> S["序列化并原子写入"]
R --> S
S --> Done(["完成"])

依赖关系分析

  • 设备认证与网关鉴权
    • 设备认证载荷与本地存储被网关用于 WS 连接鉴权与速率限制
  • 角色策略与方法授权
    • 角色策略为方法级授权提供基础,确保最小权限
  • 渠道策略与命令门禁
    • DM/群组策略与命令门禁共同构成渠道访问控制闭环
  • 凭据与令牌
    • OAuth 与 API 密钥解析贯穿模型调用链路,影响认证与授权决策
  • 会话与审计
    • 会话存储为审计与追踪提供持久化基础
graph LR
DA["设备认证载荷"] --> AC["WS 鉴权"]
DS["设备认证存储"] --> AC
RP["角色策略"] --> AC
DM["DM/群组策略"] --> CMD["命令门禁"]
CMD --> AC
OA["OAuth/令牌"] --> MA["模型认证解析"]
MA --> AC
SS["会话存储"] --> AC

性能考量

  • 会话存储缓存
    • 默认 TTL 为约 45 秒,可通过环境变量调整;缓存命中可显著降低磁盘 IO
  • 并发与锁队列
    • 会话存储采用写锁队列,避免竞态;Windows 下具备重试与退避策略
  • 速率限制
    • 设备令牌鉴权前先检查速率限制,失败即快速拒绝,降低无效验证开销

沙箱安全

围绕沙箱安全的关键目录与文件如下:

  • 镜像构建:Dockerfile.sandbox、Dockerfile.sandbox-browser、Dockerfile.sandbox-common 及其构建脚本
  • 运行时沙箱:src/agents/sandbox 下的安全校验、网络模式、主机路径处理、文件系统边界检查、Docker 容器编排等
  • 文档:docs/gateway/sandboxing.md(沙箱使用与配置)、docs/cli/sandbox.md(CLI 管理)
  • 测试:src/agents/bash-tools.exec.path.test.ts(工具执行权限测试)
graph TB
subgraph "镜像与构建"
A["Dockerfile.sandbox"]
B["Dockerfile.sandbox-browser"]
C["Dockerfile.sandbox-common"]
D["scripts/sandbox-setup.sh"]
E["scripts/sandbox-browser-setup.sh"]
F["scripts/sandbox-common-setup.sh"]
end
subgraph "运行时沙箱"
G["docker.ts<br/>容器编排与创建"]
H["validate-sandbox-security.ts<br/>安全校验"]
I["network-mode.ts<br/>网络模式判定"]
J["host-paths.ts<br/>主机路径归一化"]
K["fs-bridge-path-safety.ts<br/>文件边界检查"]
L["constants.ts<br/>默认值与常量"]
M["types.ts<br/>类型定义"]
end
subgraph "文档与测试"
N["docs/gateway/sandboxing.md"]
O["docs/cli/sandbox.md"]
P["bash-tools.exec.path.test.ts"]
end
A --> D
B --> E
C --> F
D --> G
E --> G
F --> G
G --> H
H --> I
H --> J
G --> K
G --> L
G --> M
N --> G
O --> G
P --> G

核心组件

  • 镜像与运行时
    • 基础镜像:debian:bookworm-slim,分别构建 openclaw-sandbox、openclaw-sandbox-browser、openclaw-sandbox-common
    • 构建脚本:scripts/sandbox-*.sh 负责镜像构建与缓存优化
  • 安全校验
    • 绑定挂载安全:禁止危险源路径、保留目标路径、允许根外挂载的受控开关
    • 网络模式安全:禁用 host 与 container:* 命名空间加入
    • 安全选项:禁用 unconfined seccomp/apparmor
  • 文件系统边界
    • 通过边界读取与符号链接解析,确保宿主路径在限定挂载内
  • 容器编排
    • 创建/启动/重启/重建策略;配置哈希一致性校验;注册表记录状态
  • 类型与常量
    • 默认镜像、前缀、端口、工作目录、工具白名单/黑名单、修剪策略

架构总览

下图展示从配置到容器运行、再到安全校验与文件系统边界的整体流程。

graph TB
CFG["配置<br/>agents.defaults.sandbox"] --> HASH["计算配置哈希"]
HASH --> REG["注册表<br/>containers.json"]
CFG --> IMG["镜像选择<br/>openclaw-sandbox(-common/-browser)"]
IMG --> BUILD["构建脚本<br/>sandbox-*.sh"]
CFG --> DOCKER["docker.ts<br/>构建创建参数"]
DOCKER --> SEC["validate-sandbox-security.ts<br/>安全校验"]
SEC --> NET["network-mode.ts<br/>网络模式判定"]
SEC --> HOST["host-paths.ts<br/>主机路径归一化"]
DOCKER --> RUN["创建/启动容器"]
RUN --> FS["fs-bridge-path-safety.ts<br/>文件边界检查"]
FS --> MON["执行监控/日志"]

详细组件分析

组件A:容器编排与生命周期(docker.ts)

  • 关键职责
    • 解析 Docker 可执行程序(含 Windows 兼容)
    • 执行 docker 原子命令并处理错误码与超时/中止信号
    • 构建容器创建参数(标签、只读根、tmpfs、网络、用户、环境变量、能力集、安全选项、DNS、主机、PID/内存/CPU/ulimit、绑定挂载)
    • 确保镜像存在或拉取默认镜像
    • 容器存在性/运行态检查与自动启动
    • 基于配置哈希的热容器提示与重建策略
    • 注册表更新与修剪策略
  • 安全要点
    • 在构建参数阶段即调用 validateSandboxSecurity,阻断危险配置
    • 环境变量清洗,阻断敏感变量注入
    • 默认 no-new-privileges、drop cap、可选 seccomp/apparmor
    • PID/内存/CPU/ulimit 限制
  • 生命周期
    • ensureSandboxContainer:按作用域(会话/代理/共享)命名与查找容器
    • 若配置哈希不一致且容器近期活跃,输出重建提示;否则删除旧容器并重建
    • 启动后可执行 setupCommand(需网络与写权限)
sequenceDiagram
participant Cfg as "配置"
participant Hash as "配置哈希"
participant Reg as "注册表"
participant Img as "镜像"
participant Args as "构建参数"
participant Sec as "安全校验"
participant Dkr as "Docker"
participant FSB as "文件边界"
Cfg->>Hash : 计算配置哈希
Hash->>Reg : 读取/比较当前哈希
Reg-->>Cfg : 哈希匹配/不匹配
Cfg->>Img : 确认镜像存在
Img-->>Cfg : 不存在则拉取默认镜像
Cfg->>Args : 生成创建参数
Args->>Sec : validateSandboxSecurity
Sec-->>Args : 通过/抛错
Args->>Dkr : docker create/start
Dkr-->>FSB : 启动后执行工具命令
FSB-->>Dkr : 边界检查通过

组件B:安全校验(validate-sandbox-security.ts)

  • 绑定挂载安全
    • 字符串级快速检查:非绝对源路径、覆盖系统根、命中黑名单路径
    • 允许根外挂载的受控开关
    • 保留目标路径检测(如 /workspace、/agent)
    • 符号链接逃逸硬化工序:通过现有祖先解析真实路径再校验
  • 网络模式安全
    • 禁止 host
    • 禁止 container:*(除非显式允许)
  • 安全选项
    • 禁止 unconfined seccomp/apparmor
  • 错误信息明确指出违规原因与修复建议
flowchart TD
Start(["开始"]) --> Parse["解析 bind 规范"]
Parse --> CheckAbs{"源路径绝对?"}
CheckAbs --> |否| ErrAbs["报错:非绝对路径"]
CheckAbs --> |是| Normalize["归一化主机路径"]
Normalize --> Blocked{"命中黑名单路径?"}
Blocked --> |是| ErrBlocked["报错:黑名单路径"]
Blocked --> |否| AllowedRoots{"允许根外挂载?"}
AllowedRoots --> |否| Outside{"是否在允许根内?"}
Outside --> |否| ErrOutside["报错:超出允许根"]
Outside --> |是| Reserved{"是否保留目标路径?"}
AllowedRoots --> |是| Reserved
Reserved --> |是| ErrReserved["报错:保留目标路径"]
Reserved --> |否| Symlink["通过现有祖先解析真实路径"]
Symlink --> Recheck["再次强制策略校验"]
Recheck --> Done(["结束"])

组件C:网络访问控制(network-mode.ts)

  • 归一化网络模式字符串
  • 判定 host 与 container:* 命名空间加入风险
  • 提供 isDangerousNetworkMode 快速判断

组件D:主机路径处理(host-paths.ts)

  • 归一化 POSIX 主机路径(兼容 Windows 前缀)
  • 通过现有祖先解析路径,避免末级不存在导致的绕过

组件E:文件系统边界与权限(fs-bridge-path-safety.ts)

  • 依据容器内路径解析挂载点,确保操作在限定挂载范围内
  • 使用边界读取工具进行宿主路径合法性检查
  • 支持可选别名策略(如允许最终符号链接用于 unlink)
  • 写入权限要求与只读挂载校验
  • 解析规范化的容器路径,避免符号链接逃逸

组件F:镜像与构建(Dockerfile 与脚本)

  • 基础镜像:debian:bookworm-slim
  • 浏览器镜像:包含 Chromium、VNC、noVNC、Xvfb 等
  • 通用镜像:预装 curl、wget、jq、nodejs、python3、git、pnpm、bun、brew 等
  • 构建脚本支持缓存优化与多平台构建

组件G:类型与常量(types.ts、constants.ts)

  • 类型:工具策略、作用域、工作区访问、浏览器配置、修剪策略、上下文
  • 常量:默认镜像、前缀、端口、工作目录、工具白/黑名单、注册表路径

依赖关系分析

  • docker.ts 依赖 validate-sandbox-security.ts(安全校验)、network-mode.ts(网络模式)、host-paths.ts(路径处理)、fs-bridge-path-safety.ts(文件边界)、constants.ts(默认值)、types.ts(类型)
  • 文档 sandboxing.md 与 cli/sandbox.md 为使用与运维参考
  • 测试 bash-tools.exec.path.test.ts 验证工具执行权限与沙箱主机策略
graph LR
docker_ts["docker.ts"] --> validate_ts["validate-sandbox-security.ts"]
docker_ts --> network_ts["network-mode.ts"]
docker_ts --> hostpaths_ts["host-paths.ts"]
docker_ts --> fsbridge_ts["fs-bridge-path-safety.ts"]
docker_ts --> constants_ts["constants.ts"]
docker_ts --> types_ts["types.ts"]
docs_md["docs/gateway/sandboxing.md"] --> docker_ts
cli_md["docs/cli/sandbox.md"] --> docker_ts
test_ts["bash-tools.exec.path.test.ts"] --> docker_ts

性能考量

  • 镜像构建缓存
    • 使用 buildx 与 cache-from/cache-to 降低重复构建时间
    • 多阶段构建与包管理器缓存(apt cache、lists)提升安装效率
  • 容器复用与修剪
    • 热容器窗口内延迟重建,减少频繁创建开销
    • 闲置/最大年龄修剪策略平衡资源占用
  • 资源限制
    • PID/内存/CPU/ulimit 显式设置,防止资源滥用
  • 网络隔离
    • 默认无网络或专用桥接网络,降低出站流量与暴露面

威胁防护

OpenClaw 将“路由/认证”“会话隔离”“工具执行”“外部内容”“供应链”等环节划分为多道信任边界,围绕这些边界构建了安全控制点与检测/阻断逻辑。关键安全相关模块包括:

  • 外部内容包装与检测:src/security/external-content.ts
  • 执行审批与白名单:src/infra/exec-approvals.ts
  • 主机环境变量安全:src/infra/host-env-security.ts
  • 路径规范化与保护:src/gateway/security-path.ts
  • 沙箱工具策略:src/agents/sandbox-tool-policy.ts
  • 沙箱能力导出与类型:src/agents/sandbox.ts
  • 安全策略与范围:SECURITY.md 与 docs/security/THREAT-MODEL-ATLAS.md
graph TB
subgraph "外部域"
U["用户/攻击者"]
end
subgraph "网关层"
GW["Gateway 认证/路由"]
SEC_PATH["路径规范化/保护"]
end
subgraph "会话层"
SES["会话隔离<br/>工具策略"]
end
subgraph "执行层"
APPR["执行审批/白名单"]
SBPOL["沙箱工具策略"]
SB["沙箱运行时"]
end
subgraph "外部内容"
EC["外部内容包装/检测"]
end
U --> GW
GW --> SEC_PATH
GW --> SES
SES --> APPR
SES --> EC
APPR --> SBPOL
SBPOL --> SB
EC --> SES

核心组件

  • 外部内容过滤与异常模式检测:对来自邮件、Webhook、网页抓取等来源的内容进行边界标记、元数据注入与安全提示,同时检测可疑指令模式(如“忽略先前指令”“system:”等),作为监控与阻断依据。
  • 执行审批与白名单:通过可配置的安全级别(deny/allowlist/full)与“询问策略”(off/on-miss/always),在命令执行前进行人工确认或自动放行;支持持久化存储与套接字交互。
  • 主机环境变量安全:对危险环境变量键/前缀进行拦截,限制 PATH 覆盖,仅允许受控的终端/语言类变量,降低注入与提权风险。
  • 路径规范化与保护:对输入路径进行多轮解码、规范化、点段解析与大小写/分隔符处理,生成候选路径集合,用于判定是否命中受保护前缀,阻断路径遍历。
  • 沙箱工具策略:基于全局与代理级配置合并策略,计算最终工具允许/拒绝集合,遵循“最小授权”原则。
  • 威胁建模与风险矩阵:基于 MITRE ATLAS 的威胁识别、影响与缓解建议,明确关键攻击链与优先级。

架构总览

下图展示从“外部输入”到“工具执行”的关键安全控制点,以及各威胁场景下的阻断路径。

sequenceDiagram
participant U as "用户/攻击者"
participant GW as "Gateway"
participant SEC as "外部内容过滤"
participant APPR as "执行审批"
participant POL as "工具策略"
participant SB as "沙箱运行时"
U->>GW : 发送消息/触发工具调用
GW->>SEC : 包装并检测外部内容
SEC-->>GW : 返回带边界标记与警告的内容
GW->>APPR : 解析命令与参数
APPR-->>GW : 决策允许/询问/拒绝
GW->>POL : 计算工具可用性
POL-->>GW : 最终工具集
GW->>SB : 在沙箱中执行受限工具
SB-->>GW : 输出结果
GW-->>U : 安全响应

详细组件分析

组件A:外部内容过滤与异常行为识别

  • 功能要点
    • 可疑模式检测:内置正则集合识别提示注入迹象(如“忽略先前指令”“system:”等),记录匹配模式用于监控与审计。
    • 边界标记与元数据:为外部内容注入唯一随机 ID 的起止边界标记,附加来源、发件人、主题等元信息;对常见同形异体字符进行折叠处理,防止绕过。
    • 安全提示:在包装后的内容中加入安全声明,明确不可将其视为系统指令或命令。
    • Web 工具包装:针对 web_search/web_fetch 提供差异化包装策略,后者额外包含安全提示。
  • 异常行为识别
    • 检测到可疑模式时,仍进行安全包装与处理,避免直接信任外部输入;同时保留日志以便进一步分析。
  • 自动响应机制
    • 该模块本身不直接阻断,但其输出被后续审批与策略模块消费,构成“先包装、再决策”的闭环。
flowchart TD
Start(["接收外部内容"]) --> Detect["检测可疑模式"]
Detect --> HasSuspicion{"是否匹配可疑模式?"}
HasSuspicion --> |是| Log["记录匹配模式"]
HasSuspicion --> |否| Wrap["包装内容边界标记+元数据+安全提示"]
Log --> Wrap
Wrap --> Return["返回安全内容"]

组件B:执行审批与白名单(含路径与参数校验)

  • 功能要点
    • 安全级别:deny(默认拒绝)、allowlist(白名单放行)、full(完全开放)。
    • 询问策略:off(不询问)、on-miss(未命中白名单时询问)、always(每次询问)。
    • 命令分析与计划:记录 argv、cwd、原始命令、会话标识、节点信息等,便于审计与回溯。
    • 允许列表:支持按模式匹配的条目,持久化存储于专用文件,具备去重与 ID 自动生成。
    • 套接字交互:通过命名套接字与令牌进行请求/决策通信,支持超时控制。
  • 风险控制
    • 对高危命令采用“白名单+询问”策略,降低误执行风险;支持记录最近使用与解析路径,辅助审计。
    • 默认 deny 与 on-miss 策略在未命中白名单时触发人工确认,有效阻断路径操纵与命令混淆。
flowchart TD
Req["收到执行请求"] --> Analyze["分析命令与参数"]
Analyze --> Resolve["解析安全级别与询问策略"]
Resolve --> Decision{"是否命中白名单且满足安全级别?"}
Decision --> |是| Allow["允许执行"]
Decision --> |否| Ask{"询问策略触发?"}
Ask --> |是| Socket["通过套接字请求决策"]
Socket --> Decide{"决策:允许/拒绝"}
Decide --> |允许| Allow
Decide --> |拒绝| Deny["拒绝执行"]
Ask --> |否| Deny

组件C:主机环境变量安全(阻断注入与提权)

  • 功能要点
    • 危险键/前缀拦截:对敏感环境变量键与前缀进行黑名单管理,避免注入 PATH、LD_PRELOAD 等高危变量。
    • Shell 包装器变量白名单:仅允许 TERM、LANG、LC_* 等终端/本地化变量,减少副作用。
    • 合并与过滤:从进程环境与覆盖变量中合并,剔除危险项,严格禁止 PATH 覆盖。
  • 风险控制
    • 通过严格的键名规范化与前缀匹配,阻断提示注入与动态链接劫持;仅在必要时允许有限变量覆盖,降低提权与逃逸风险。
flowchart TD
Env["读取进程环境"] --> Merge["合并基础环境与覆盖变量"]
Merge --> Filter["过滤危险键/前缀与覆盖键"]
Filter --> BlockPath{"是否覆盖 PATH?"}
BlockPath --> |是| Drop["丢弃覆盖"]
BlockPath --> |否| Keep["保留变量"]
Drop --> Output["输出安全环境"]
Keep --> Output

组件D:路径规范化与保护(阻断路径遍历)

  • 功能要点
    • 多轮解码:对输入路径进行最多 N 次解码尝试,记录解码轮次与是否达到上限。
    • 规范化:统一分隔符、去除尾随斜杠、小写化;生成候选路径集合。
    • 点段解析:使用 URL 解析消除相对路径段,确保规范化结果稳定。
    • 前缀匹配:对受保护前缀(如特定 API 路由)进行匹配判断,若命中则判定为受保护。
    • 异常判定:当出现畸形编码或解码轮次达到上限时,采取“关闭”策略(即判定为受保护/异常)。
  • 风险控制
    • 通过候选集与异常判定,有效阻断“../..”“%2e%2e%2f”等路径遍历变种;在边界不明确时倾向保守处理。
flowchart TD
In["输入路径"] --> Decode["多轮解码并记录状态"]
Decode --> Normalize["规范化分隔符/大小写/点段解析"]
Normalize --> Candidates["生成候选路径集合"]
Candidates --> Protect{"是否命中受保护前缀?"}
Protect --> |是| Block["判定为受保护/阻断"]
Protect --> |否| CheckAnomaly{"是否畸形编码/达到解码上限?"}
CheckAnomaly --> |是| Block
CheckAnomaly --> |否| Allow["允许访问"]

组件E:沙箱工具策略(最小授权与策略合并)

  • 功能要点
    • 配置来源:支持全局与代理级 allow/deny/alsoAllow 组合;alsoAllow 在无显式 allow 时视作在隐式“全部”基础上累加。
    • 合并规则:代理策略优先于全局策略,最终策略取交集(更严格者胜)。
    • 导出与类型:统一导出策略解析函数与类型定义,便于上层工具调用与审计。
  • 风险控制
    • 通过“代理覆盖 > 全局 > 默认”的层级合并,确保细粒度策略落地;拒绝集合优先,避免过度授权。
classDiagram
class SandboxToolPolicyConfig {
+allow? : string[]
+alsoAllow? : string[]
+deny? : string[]
}
class SandboxToolPolicy {
+allow : string[]
+deny : string[]
}
class pickSandboxToolPolicy {
+(config?) : SandboxToolPolicy|undefined
}
SandboxToolPolicyConfig --> pickSandboxToolPolicy : "输入"
pickSandboxToolPolicy --> SandboxToolPolicy : "输出"

组件F:威胁情报集成、实时监控与告警

  • 威胁情报集成
    • ClawHub 供应链扫描:计划引入 VirusTotal 行为分析与社区举报/审计日志,逐步替代简单正则模式。
  • 实时监控与告警
    • 外部内容可疑模式检测:记录匹配模式,作为异常事件上报与趋势分析依据。
    • 执行审批:通过套接字与持久化文件记录决策与使用情况,支持审计与回溯。
  • 建议
    • 结合外部威胁情报源(如 VT、开源情报)对已发布技能进行扫描与评分;在网关层增加速率限制与成本预算,降低资源耗尽风险。

组件G:自动响应机制(阻断与降级)

  • 外部内容:包装并注入安全提示,降低注入指令的可执行性。
  • 执行审批:deny 策略直接阻断;allowlist 策略在未命中时触发询问;always 策略强制人工确认。
  • 沙箱策略:拒绝集合优先,最小授权暴露工具集;代理策略可叠加 alsoAllow,但最终以更严格为准。
  • 路径保护:异常判定与前缀匹配导致的阻断,避免路径遍历与越权访问。

依赖关系分析

  • 组件耦合
    • 外部内容过滤与执行审批:前者提供“安全内容”,后者据此做“是否允许”的决策,耦合度中等。
    • 路径保护与沙箱策略:路径保护用于边界判定,沙箱策略用于工具可用性控制,二者共同决定访问与执行面。
    • 主机环境变量安全:在系统命令执行前对环境进行清洗,降低注入风险,与执行审批形成互补。
  • 依赖可视化
graph LR
EC["external-content.ts"] --> APPR["exec-approvals.ts"]
SEC_PATH["security-path.ts"] --> APPR
SEC_PATH --> SBPOL["sandbox-tool-policy.ts"]
SBPOL --> SB["sandbox.ts"]
ENV["host-env-security.ts"] --> APPR

性能考量

  • 外部内容包装:多轮解码与候选集生成存在时间复杂度,建议在高频路径上缓存标准化结果与标记替换。
  • 执行审批:套接字交互与文件读写带来延迟,可通过批量决策与本地缓存优化;同时注意文件权限与时钟同步。
  • 路径保护:URL 解析与候选集生成开销可控,建议在网关入口集中处理,避免重复计算。
  • 沙箱策略:策略合并与类型检查为纯内存操作,性能影响较小;建议在配置变更时进行一次性校验。

故障排查指南

  • 外部内容告警过多
    • 检查可疑模式正则是否过于宽泛;结合业务场景调整 includeWarning 与元数据注入策略。
    • 关注边界标记折叠逻辑,确保同形异体字符被正确归一化。
  • 执行审批误判
    • 核对 allowlist 条目是否命中;检查 ask 与 security 策略组合;确认持久化文件权限与格式。
    • 使用套接字交互时,确认令牌与路径配置正确,超时设置合理。
  • 路径遍历失败
    • 检查输入路径是否包含畸形编码或过多点段;确认受保护前缀配置是否覆盖目标路由。
  • 沙箱工具不可用
    • 核对代理策略与全局策略合并结果;确认 alsoAllow 是否导致意外放行或被更严格策略覆盖。
  • 环境变量异常
    • 检查危险键/前缀是否被错误放行;确认 PATH 是否被覆盖;核对 shell 包装器白名单。

威胁建模与攻击链

  • 关键攻击链
    • 技能型数据窃取:发布恶意技能 → 绕过审核 → 技能执行时窃取凭据。
    • 提示注入到远程命令执行:直接提示注入 → 绕过执行审批 → 在宿主或沙箱中执行命令。
    • 间接注入 via 外部内容:污染 URL 内容 → Agent 获取并执行 → 数据外泄。
  • 风险矩阵(部分)
    • T-EXEC-001、T-PERSIST-001、T-EXFIL-003:高影响/高概率,优先处置。
    • T-EXEC-002、T-EXEC-004、T-ACCESS-003:中高风险,需强化审批与白名单。
    • T-IMPACT-002:高概率/中影响,需引入速率限制与成本预算。

漏洞评估与加固清单

  • 外部内容
    • 引入 AST/行为分析替代简单正则;增强边界标记的抗同形异体攻击能力。
  • 执行审批
    • 默认 deny + on-miss;对高危命令启用 always;完善套接字交互与持久化文件权限。
  • 路径保护
    • 明确受保护前缀清单;对异常路径采取保守阻断策略。
  • 沙箱策略
    • 代理策略优先;拒绝集合优先;定期审计工具暴露面。
  • 环境变量
    • 严格拦截危险键/前缀;禁止 PATH 覆盖;仅允许必要变量。
  • 运维与合规
    • 定期安全审计与修复;实施最小权限与只读根文件系统;容器运行时限制能力与网络。
❌
❌