微信扫码
添加专属顾问
我要投稿
揭秘OpenClaw沙箱系统的企业级安全架构,看阿里如何打造AI智能体安全防线。 核心内容: 1. OpenClaw沙箱系统的核心安全机制与隔离策略 2. 多层级容器管理与工具权限控制体系 3. 实战配置示例与安全最佳实践
三、各系统模块讲解
3.8 SandBox 沙箱系统
Sandbox 是 OpenClaw 的 Docker 隔离层,用于在容器中执行 AI Agent 的工具操作,而非直接在主机上运行。
src/agents/sandbox/├── types.ts # 核心类型定义├── config.ts # 配置合并逻辑├── context.ts # 入口点 - 解析沙箱上下文├── docker.ts # Docker 容器管理├── browser.ts # 隔离浏览器容器├── tool-policy.ts # 工具允许/拒绝策略├── validate-sandbox-security.ts # 安全验证├── fs-bridge.ts # 文件系统操作桥接└── prune.ts # 容器自动清理
禁止的绑定挂载:
禁止的网络模式:
默认安全配置:
隔离时工具过滤顺序:
默认允许的工具:exec、read、write、edit、apply_patch、image 等
默认禁止的工具:browser、canvas、nodes、cron、gateway 及所有消息通道
{ "agents": { "defaults": { "sandbox": { "mode": "non-main", "scope": "session", "workspaceAccess": "none", "docker": { "image": "openclaw-sandbox:bookworm-slim", "network": "none", "memory": "512m", "cpus": 1 }, "prune": { "idleHours": 24, "maxAgeDays": 7 } } } }}3.9 记忆管理
OpenClaw 的记忆系统采用 "文件即真相" 的设计哲学:
~/.openclaw/workspace/├── MEMORY.md # 长期记忆(精选、持久化)└── memory/ └── YYYY-MM-DD.md # 每日记忆日志(append-only)
分层设计:
type MemorySource = "memory" | "sessions";type MemorySearchResult = { path: string; // 文件路径 startLine: number; // 起始行号 endLine: number; // 结束行号 score: number; // 相关性得分 snippet: string; // 文本片段(~700 字符) source: MemorySource; // 来源类型 citation?: string; // 引用标注};记忆索引管理器 (MemoryIndexManager)
核心职责:
关键特性:
记忆搜索工具
memory_search - 语义搜索
memory_get - 定向读取
搜索流程
用户查询 ↓关键词提取 (FTS)向量嵌入 ↓并行搜索 ├─ 向量搜索(语义相似) └─ BM25 搜索(关键词匹配) ↓加权融合 ↓后处理 ├─ 时间衰减 └─ MMR 去重 ↓Top-K 结果
为什么需要混合搜索?
向量搜索优势:
向量搜索劣势:
BM25 优势:
分数融合算法
finalScore = vectorWeight * vectorScore + textWeight * textScore
权重归一化为 1.0,默认 vectorWeight=0.7,textWeight=0.3
MMR(最大边际相关性)去重
目的:避免返回重复或高度相似的片段
算法:
score = λ × relevance - (1-λ) × max_similarity_to_selected
λ 参数:
查询:"家庭网络设置"无 MMR:1. memory/2026-02-10.md (0.92) ← 路由器 + VLAN2. memory/2026-02-08.md (0.89) ← 路由器 + VLAN(重复!)3. memory/network.md (0.85)有 MMR (λ=0.7):1. memory/2026-02-10.md (0.92) ← 路由器 + VLAN2. memory/network.md (0.85) ← 参考文档(多样)3. memory/2026-02-05.md (0.78) ← AdGuard DNS(多样)
时间衰减
目的:让近期记忆排名更高
公式:
decayedScore = score × e^(-λ × ageInDays)
半衰期:默认 30 天
常青文件(不衰减):
支持的提供商
自动选择逻辑
if (local.modelPath 存在) return "local";if (OpenAI key 可用) return "openai";if (Gemini key 可用) return "gemini";if (Voyage key 可用) return "voyage";if (Mistral key 可用) return "mistral";return disabled;
批量索引
SQLite 数据库结构
-- 文件表CREATE TABLE files ( path TEXT PRIMARY KEY, source TEXT, mtime INTEGER, hash TEXT);-- 分块表CREATE TABLE chunks ( id TEXT PRIMARY KEY, path TEXT, startLine INTEGER, endLine INTEGER, text TEXT, embedding BLOB, source TEXT);-- 向量表CREATE VIRTUAL TABLE chunks_vec USING vec0(...);-- 全文索引CREATE VIRTUAL TABLE chunks_fts USING fts5(...);-- Embedding 缓存CREATE TABLE embedding_cache ( hash TEXT PRIMARY KEY, embedding BLOB);
索引更新策略
触发条件:
会话索引:
预压缩提示
触发时机:会话接近自动压缩时
机制:
tokenEstimate > contextWindow - reserveTokensFloor - softThresholdTokens
行为:
配置示例
{ agents: { defaults: { compaction: { reserveTokensFloor: 20000, memoryFlush: { enabled: true, softThresholdTokens: 4000, systemPrompt: "Session nearing compaction...", prompt: "Write any lasting notes to memory/YYYY-MM-DD.md..." } } } }}何时写入记忆
配置建议
小型语料库:
{ "provider": "openai", "query": { "hybrid": { "enabled": false } }}大型语料库 + 每日笔记:
{ "provider": "openai", "remote": { "batch": { "enabled": true } }, "query": { "hybrid": { "enabled": true, "mmr": { "enabled": true, "lambda": 0.7 }, "temporalDecay": { "enabled": true, "halfLifeDays": 30 } } }}完全本地:
{ "provider": "local", "fallback": "none", "local": { "modelPath": "hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF" }}Embedding 缓存
{ "cache": { "enabled": true, "maxEntries": 50000 }}好处:
sqlite-vec 加速
{ "store": { "vector": { "enabled": true, "extensionPath": "/path/to/sqlite-vec" } }}好处:
# 查看状态openclaw memory status# 强制同步openclaw memory sync --force# 检查配置openclaw config get agents.defaults.memorySearch
OpenClaw 的记忆管理系统是一个生产级的混合记忆解决方案,核心优势:
这个设计体现了现代 AI Agent 记忆管理的最佳实践:让文件成为真相,让索引成为加速器。
3.10 Skills 模块详解
Skills 模块位于 src/agents/skills/,是 OpenClaw Agent 能力扩展的核心模块。
Skill = 一个封装了特定能力的 Markdown 文件 (SKILL.md),包含:
src/agents/skills/├── types.ts # 类型定义├── config.ts # 配置解析与过滤├── workspace.ts # 核心加载逻辑├── frontmatter.ts # SKILL.md 解析├── filter.ts # 技能过滤器├── bundled-dir.ts # 内置技能目录解析├── bundled-context.ts # 内置技能缓存├── plugin-skills.ts # 插件技能集成├── refresh.ts # 文件监听与版本刷新├── env-overrides.ts # 环境变量注入├── serialize.ts # 并发控制锁├── tools-dir.ts # 工具目录路径└── skills-install.ts # 技能安装器
// workspace.ts:369-388extra → bundled → managed → agents-skills-personal → agents-skills-project → workspace
type SkillEntry = { skill: Skill; frontmatter: ParsedSkillFrontmatter; metadata?: OpenClawSkillMetadata; invocation?: SkillInvocationPolicy;};type OpenClawSkillMetadata = { always?: boolean; skillKey?: string; primaryEnv?: string; emoji?: string; os?: string[]; requires?: { bins?: string[]; env?: string[]; config?: string[]; }; install?: SkillInstallSpec[];};type SkillSnapshot = { prompt: string; skills: Array<{ name, primaryEnv, requiredEnv }>; skillFilter?: string[]; resolvedSkills?: Skill[]; version?: number;};// config.ts:70-101shouldIncludeSkill() 检查:1. config.skills.entries[skillKey].enabled !== false2. bundled allowlist 检查3. 运行时资格评估: - OS 兼容性 - 二进制依赖 - 环境变量依赖 - 配置路径检查
---name: githubdescription: "GitHub operations via `gh` CLI..."metadata: openclaw: emoji: "🐙" requires: bins: ["gh"] install: - id: brew kind: brew formula: gh bins: ["gh"]---# GitHub Skill...使用说明...
// skills.ts 导出loadWorkspaceSkillEntries() // 加载技能条目buildWorkspaceSkillSnapshot() // 构建快照(含 prompt)buildWorkspaceSkillsPrompt() // 生成 Agent promptfilterWorkspaceSkillEntries() // 过滤技能buildWorkspaceSkillCommandSpecs() // 生成命令规格syncSkillsToWorkspace() // 同步到沙箱applySkillEnvOverrides() // 注入环境变量
// types.ts:3-17type SkillInstallSpec = { kind: "brew" | "node" | "go" | "uv" | "download"; formula?: string; // brew formula package?: string; // npm/go/uv 包名 module?: string; // go module url?: string; // download URL // ...};// refresh.tsensureSkillsWatcher() // 监听 SKILL.md 变更 → bumpSkillsSnapshotVersion() // 版本号递增 → 事件通知 listeners
3.11 Session 管理
┌─────────────────────────────────────────────────────────────────┐│ 会话管理层级 │├─────────────────────────────────────────────────────────────────┤│ Session Key(身份标识) ││ ↓ ││ Session Entry(会话元数据) ││ ↓ ││ Session Store(持久化存储) ││ ↓ ││ Transcript File(对话历史) │└─────────────────────────────────────────────────────────────────┘
1. 会话键格式规范
基础格式: agent:<agentId>:<rest>示例:- agent:main:main # 默认 agent 的主会话- agent:ops:work # ops agent 的主会话- agent:main:telegram:direct:user123 # Telegram DM 会话- agent:main:discord:group:guild789 # Discord 群组会话- agent:main:cron:daily-backup:run:uuid # Cron 任务运行会话- agent:main:subagent:child-session # 子代理会话
2. 会话键解析(session-key-utils.ts:12-32)
function parseAgentSessionKey(sessionKey: string | undefined | null): ParsedAgentSessionKey | null { // 1. 规范化:小写、去空格 const raw = (sessionKey ?? "").trim().toLowerCase(); // 2. 验证最小结构 (agent:id:rest) const parts = raw.split(":").filter(Boolean); if (parts.length < 3 || parts[0] !== "agent") return null; // 3. 提取 agentId 和 rest return { agentId: parts[1], rest: parts.slice(2).join(":") };}3. 会话类型判断
核心字段(config/sessions/types.ts 相关)
type SessionEntry = { // === 身份标识 === sessionId: string; // UUID,关联对话历史文件 sessionFile?: string; // 自定义会话文件路径 // === 模型配置 === model?: string; // 当前运行时模型 modelProvider?: string; // 当前运行时提供商 modelOverride?: string; // 用户指定的模型覆盖 providerOverride?: string;// 用户指定的提供商覆盖 contextTokens?: number; // 上下文窗口大小 // === Token 统计 === totalTokens?: number; // 总 token 数 inputTokens?: number; // 输入 token outputTokens?: number; // 输出 token totalTokensFresh?: boolean; // token 数据是否新鲜 // === 投递路由 === lastChannel?: string; // 最后使用的通道 lastTo?: string; // 最后发送目标 lastAccountId?: string; // 最后账号 ID lastThreadId?: string; // 最后线程 ID deliveryContext?: DeliveryContext; // 投递上下文 // === 会话状态 === updatedAt?: number; // 最后更新时间戳 systemSent?: boolean; // 系统消息是否已发送 abortedLastRun?: boolean; // 上次运行是否中断 // === 行为配置 === thinkingLevel?: string; // 思考级别 verboseLevel?: string; // 详细级别 reasoningLevel?: string; // 推理级别 sendPolicy?: string; // 发送策略 // === 群组/频道元数据 === chatType?: "direct" | "group" | "channel"; channel?: string; // 来源通道 subject?: string; // 主题/名称 groupId?: string; // 群组 ID groupChannel?: string; // 频道名称 space?: string; // 空间/工作区 // === 显示 === displayName?: string; // 显示名称 label?: string; // 用户标签 // === 线程/派生 === forkedFromParent?: boolean; // 是否从父会话派生 spawnedBy?: string; // 创建来源 // === 压缩状态 === compactionCount?: number; // 压缩次数 memoryFlushAt?: number; // 内存刷新时间};1. 会话初始化流程(auto-reply/reply/session.ts:165-579)
┌─────────────────────────────────────────────────────────────────┐│ 会话初始化流程 │├─────────────────────────────────────────────────────────────────┤│ 1. 解析 Agent ID ││ resolveSessionAgentId({ sessionKey, config }) ││ ││ 2. 加载会话存储 ││ loadSessionStore(storePath, { skipCache: true }) ││ ││ 3. 检查重置触发器 ││ 匹配 /new, /reset 等命令 ││ ││ 4. 评估会话新鲜度 ││ evaluateSessionFreshness({ updatedAt, now, policy }) ││ ││ 5. 处理线程派生 (可选) ││ forkSessionFromParent() ││ ││ 6. 持久化会话文件 ││ resolveAndPersistSessionFile() ││ ││ 7. 归档旧会话 (重置时) ││ archiveSessionTranscripts() ││ ││ 8. 触发插件钩子 ││ hookRunner.runSessionStart() / runSessionEnd() │└─────────────────────────────────────────────────────────────────┘2. 重置触发器机制 (auto-reply/reply/session.ts:249-273)
// 默认重置触发器const DEFAULT_RESET_TRIGGERS = ["/new", "/reset"];// 匹配逻辑for (const trigger of resetTriggers) {if (trimmedBodyLower === triggerLower) {isNewSession = true;bodyStripped = ""; // 清空消息体resetTriggered = true;}// 支持带参数的重置: "/new 你好"if (strippedForResetLower.startsWith(triggerPrefixLower)) {isNewSession = true;bodyStripped = strippedForReset.slice(trigger.length).trimStart();resetTriggered = true;}}
3. 会话新鲜度策略 (config/sessions/reset.ts 相关)
type ResetPolicy = {direct: number; // DM 会话过期时间 (小时)group: number; // 群组会话过期时间thread: number; // 线程会话过期时间};// 默认策略const DEFAULT_RESET_HOURS = { direct: 24, group: 4, thread: 24 };// 评估函数function evaluateSessionFreshness(params: {updatedAt: number;now: number;policy: ResetPolicy;}): { fresh: boolean; reason?: string };
4. 线程派生机制 (auto-reply/reply/session.ts:123-163)
// 当父会话 token 过多时跳过派生,避免上下文溢出const DEFAULT_PARENT_FORK_MAX_TOKENS = 100_000;function forkSessionFromParent(params: {parentEntry: SessionEntry;agentId: string;sessionsDir: string;}): { sessionId: string; sessionFile: string } | null {// 1. 打开父会话管理器const manager = SessionManager.open(parentSessionFile);// 2. 创建分支会话const sessionFile = manager.createBranchedSession(leafId);// 3. 或创建新的派生会话文件const header = {type: "session",version: CURRENT_SESSION_VERSION,id: sessionId,parentSession: parentSessionFile, // 链接到父会话};}
1. 存储结构(gateway/session-utils.ts:575-618)
~/.openclaw/├── agents/│ ├── main/sessions.json # main agent 的会话存储│ ├── ops/sessions.json # ops agent 的会话存储│ └── ...└── sessions.json # 全局存储 (旧版兼容)
2. 多 Agent 存储合并(gateway/session-utils.ts:575-618)
function loadCombinedSessionStoreForGateway(cfg: OpenClawConfig): { storePath: string; store: Record<string, SessionEntry>;} { // 模板路径: 支持每个 agent 独立存储 if (isStorePathTemplate(storeConfig)) { for (const agentId of agentIds) { const storePath = resolveStorePath(storeConfig, { agentId }); const store = loadSessionStore(storePath); // 合并到 combined,以 canonicalKey 为键 } } // 单一文件: 所有 agent 共享存储 else { // 直接加载,规范化键名 }}3. 键名规范化与遗留键清理(gateway/session-utils.ts:237-260)
// 清理大小写不一致的遗留键function pruneLegacyStoreKeys(params: { store: Record<string, unknown>; canonicalKey: string; candidates: Iterable<string>;}) { // 1. 收集所有需要删除的键 for (const candidate of candidates) { if (candidate !== canonicalKey) { keysToDelete.add(candidate); } // 2. 查找大小写变体 for (const legacyKey of findStoreKeysIgnoreCase(store, candidate)) { if (legacyKey !== canonicalKey) { keysToDelete.add(legacyKey); } } } // 3. 批量删除 for (const key of keysToDelete) { delete store[key]; }}Cron 会话清理器策略(cron/session-reaper.ts:57-156)
// 清理策略const DEFAULT_RETENTION_MS = 24 * 3_600_000; // 24 小时const MIN_SWEEP_INTERVAL_MS = 5 * 60_000; // 最小清理间隔 5 分钟async function sweepCronRunSessions(params: {cronConfig?: CronConfig;sessionStorePath: string;}): Promise<ReaperResult> {// 1. 节流检查if (now - lastSweepAtMs < MIN_SWEEP_INTERVAL_MS) {return { swept: false, pruned: 0 };}// 2. 遍历存储,删除过期 cron 运行会话for (const key of Object.keys(store)) {if (isCronRunSessionKey(key) && entry.updatedAt < cutoff) {delete store[key];pruned++;}}// 3. 归档关联的对话文件archiveSessionTranscripts({ sessionId, reason: "deleted" });}
1. 投递信息持久化(channels/session.ts:21-58)
async function recordInboundSession(params: { storePath: string; sessionKey: string; ctx: MsgContext; updateLastRoute?: InboundLastRouteUpdate;}): Promise<void> { // 1. 记录入站消息元数据 await recordSessionMetaFromInbound({ storePath, sessionKey: canonicalSessionKey, ctx, groupResolution, }); // 2. 更新投递路由 await updateLastRoute({ storePath, sessionKey: targetSessionKey, deliveryContext: { channel: update.channel, to: update.to, accountId: update.accountId, threadId: update.threadId, }, });}2. 投递路由解析(auto-reply/reply/session.ts:60-87)
function resolveLastChannelRaw(params: { originatingChannelRaw?: string; persistedLastChannel?: string; sessionKey?: string;}): string | undefined { // 内部 webchat/系统消息不应覆盖已知的外部投递路由 if (originatingChannel === INTERNAL_MESSAGE_CHANNEL) { // 优先使用已持久化的外部通道 if (persistedChannel && isDeliverableMessageChannel(persistedChannel)) { return persistedChannel; } // 回退到 sessionKey 中编码的通道提示 if (sessionKeyChannelHint && isDeliverableMessageChannel(sessionKeyChannelHint)) { return sessionKeyChannelHint; } }}3.12 自进化机制
OpenClaw 通过以下几个核心机制实现自我更新与进化:
工作区包含可编辑的文件,agent 可在对话中修改:
每次运行时动态构建系统提示,包括:
当文件被修改后,下次会话立即反映变化。
Agent 内置 read/write/edit 工具,可以:
系统提示包含 OpenClaw Self-Update 部分,agent 可以运行:
用户指令/反馈 → Agent 修改文件 → 下次会话加载新内容 → 行为改变 → 持续迭代
这种设计让 agent 能够像人类一样"学习"和"成长",通过文件系统持久化进化成果。
3.13 工作区与 Agent 路由
工作区是代理的文件系统级隔离环境,包含代理的引导文件、记忆、身份和工具配置。
核心特性
1. 目录结构
3. 初始化流程
.openclaw/workspace-state.json)4. 子代理过滤
路由系统决定哪个代理处理哪个消息,是 OpenClaw 多代理架构的核心。
路由层次结构(从高到低优先级)
const tiers = [ { matchedBy: "binding.peer", ... }, // 1. 精确对等匹配 { matchedBy: "binding.peer.parent", ... }, // 2. 父对等匹配(线程) { matchedBy: "binding.guild+roles", ... }, // 3. 公会+角色匹配 { matchedBy: "binding.guild", ... }, // 4. 公会匹配 { matchedBy: "binding.team", ... }, // 5. 团队匹配 { matchedBy: "binding.account", ... }, // 6. 账户匹配 { matchedBy: "binding.channel", ... }, // 7. 频道匹配 { matchedBy: "default", ... } // 8. 默认代理];路由匹配规则详解
用于线程继承
当消息来自线程但线程本身无绑定时,检查父频道绑定
确保 Discord 论坛主题能继承父频道路由
guildId 和 roles 约束Discord 公会级匹配(无角色要求)
示例:公会 g1 的所有消息路由到代理 guild
Slack 特有
匹配 Slack 工作区(团队)
无任何绑定匹配时使用
路由到配置中的 defaultAgentId(默认为 main)
绑定配置示例
const bindings = [ // 精确对等绑定 { agentId: "support", match: { channel: "whatsapp", accountId: "biz", peer: { kind: "direct", id: "+1234567890" } } }, // 公会绑定 { agentId: "community", match: { channel: "discord", guildId: "123456789" } }, // 角色绑定 { agentId: "admin", match: { channel: "discord", guildId: "123456789", roles: ["admin", "moderator"] } }];会话键决定对话历史如何隔离和持久化。
DM 作用域类型
type DmScope = | "main" // 所有 DM 共享一个会话 | "per-peer" // 每个对等有独立会话 | "per-channel-peer" // 每个频道+对等组合独立 | "per-account-channel-peer"; // 每个账户+频道+对等组合独立
会话键格式
agent:{agentId}:{mainKey} // 主会话agent:{agentId}:direct:{peerId} // per-peer DMagent:{agentId}:{channel}:direct:{peerId} // per-channel-peer DMagent:{agentId}:{channel}:{accountId}:direct:{peerId} // per-account-channel-peeragent:{agentId}:{channel}:{peerKind}:{peerId} // 群组/频道agent:{agentId}:{channel}:{peerKind}:{peerId}:thread:{threadId} // 线程身份链接
允许跨平台合并会话。
示例:Telegram 用户 111111111 和 Discord 用户 222222222222222222 共享身份 alice。
identityLinks: { alice: ["telegram:111111111", "discord:222222222222222222"]}场景 1:多租户 SaaS
binding 1: WhatsApp Business "company-a" -> agent "support-a"binding 2: WhatsApp Business "company-b" -> agent "support-b"
场景 2:Discord 社区管理
binding 1: Guild "123" + Role "admin" -> agent "admin-bot"binding 2: Guild "123" (no role) -> agent "community-bot"binding 3: Channel "welcome" -> agent "greeter"
场景 3:Slack 企业部署
binding 1: Team "T-sales" -> agent "sales-assistant"binding 2: Team "T-engineering" -> agent "tech-support"
场景 4:线程隔离
Parent Channel "general" -> agent "general-bot"Thread in "general" inherits parent binding
3.14 Nodes
Nodes 是 OpenClaw 的分布式设备/客户端管理架构,让 Gateway 可以远程控制和协调多个设备上的操作。
Node = 一个可执行命令的远程客户端,例如:
Node Gateway |-- node.pair.request -------------->| (发起请求) | | (加入 pending 列表) | | |<-------- 广播 node.pair.requested --| (通知管理员) | | | [管理员审批] | | | |<------- node.pair.approve ---------| (生成 token) | | |-- node.pair.verify --------------->| (验证 token) | | |<----------- 连接建立 --------------| (认证成功)
src/node-host/ 中的代码运行在远程设备上:
┌─────────────────────────────────────────────────┐│ Node Host │├─────────────────────────────────────────────────┤│ runner.ts ││ - 启动 WebSocket 客户端 ││ - 连接 Gateway ││ - 声明 caps/commands │├─────────────────────────────────────────────────┤│ invoke.ts ││ - 处理 system.run/which/browser.proxy ││ - 执行本地命令 ││ - 管理执行审批 │├─────────────────────────────────────────────────┤│ config.ts ││ - 存储 nodeId/token/gateway 配置 ││ - ~/.openclaw/node.json │└─────────────────────────────────────────────────┘
命令分类(gateway/node-command-policy.ts:56)
安全策略
当 Node 离线时自动唤醒 (gateway/server-methods/nodes.ts:89):
1. maybeWakeNodeWithApns()├── 检查 APNS 注册├── 发送后台唤醒通知└── 等待重连 (3s)2. 如果仍离线,重试一次└── 发送后台唤醒 + 等待 (12s)3. 如果仍离线└── maybeSendNodeWakeNudge()└── 发送可见提醒:"OpenClaw needs a quick reopen"
Node 可向 Gateway 发送事件 (gateway/server-node-events.ts:247):
客户端 → Gateway.node.invoke({nodeId, command, params}) │ ├── 检查节点是否连接 │ └── 未连接 → APNS 唤醒 │ ├── 命令策略检查 │ └── node-command-policy.ts │ ├── NodeRegistry.invoke() │ └── WebSocket 发送 node.invoke.request │ └── Node Host 处理 ├── invoke.ts 路由到具体 handler ├── 执行命令 (spawn + 安全检查) └── 返回 node.invoke.result3.15 安全策略
核心原则:OpenClaw 采用"个人助手"模型,不是多租户共享总线。
单用户信任模型:┌─────────────────────────────────────┐│ 一个可信操作者 ││ ├── 一个 Gateway ││ │ └── 多个 Agent ││ └── 多个会话(sessionKey 是路由控制,不是授权边界)└─────────────────────────────────────┘
关键点:
插件以进程内方式加载,与 Gateway 同等权限:
插件 = 可信代码├── 可以读取环境变量/文件├── 可以执行主机命令└── 与 Gateway 进程权限相同
安全报告必须证明边界绕过(如未认证加载、策略绕过),而不是"恶意插件执行了特权操作"。
agents.defaults.sandbox.mode: off # 默认关闭沙箱tools.exec.host: sandbox # 路由偏好,但沙箱未激活时在主机执行
含义:默认情况下,命令执行在主机上进行,因为操作者已受信任。
信任边界:├── 主机 OS/管理员边界 = 信任边界├── 能修改 ~/.openclaw 的人 = 可信操作者└── 推荐模式:一用户一主机/VPS一Gateway
# 推荐:仅绑定回环地址gateway.bind="loopback" # 默认值openclaw gateway run --bind loopback# 远程访问方案:# 1. SSH 隧道# 2. Tailscale serve/funnel# 3. 不要直接暴露到公网
# 推荐tools.exec.applyPatch.workspaceOnly: true # 限制写入到工作区# 可选tools.fs.workspaceOnly: true # 限制所有文件操作到工作区
总结:OpenClaw 的安全模型基于"可信操作者"假设,核心是主机信任 + Gateway 认证,不提供多租户隔离。安全报告需要证明边界绕过,而非展示已授权/可信场景下的行为。
3.16 配置管理
配置管理采用分层架构,核心模块位于 src/config/:
src/config/├── io.ts # 配置I/O核心引擎├── paths.ts # 路径解析策略├── validation.ts # 多层验证框架├── zod-schema.ts # Zod模式定义├── defaults.ts # 运行时默认值应用├── includes.ts # $include模块化支持├── env-substitution.ts # 环境变量替换└── types.ts # TypeScript类型聚合
配置文件格式与位置
JSON5 格式,支持注释和尾随逗号:
// 支持环境变量{ "models": { "providers": { "anthropic": { "apiKey": "${ANTHROPIC_API_KEY}" // 运行时替换 } } }}路径解析优先级(paths.ts:130-183):
配置生命周期
加载 → 解析 → 合并 → 验证 → 应用默认值 → 缓存
关键实现(io.ts:682-818):
function loadConfig(): OpenClawConfig { // 1. 读取原始JSON5文件 const raw = deps.fs.readFileSync(configPath, "utf-8"); const parsed = deps.json5.parse(raw); // 2. 解析$include指令 const resolved = resolveConfigIncludesForRead(parsed, configPath, deps); // 3. 环境变量替换 const { resolvedConfigRaw } = resolveConfigForRead(resolved, deps.env); // 4. 验证与默认值 const validated = validateConfigObjectWithPlugins(resolvedConfigRaw); const cfg = applyModelDefaults(applyAgentDefaults(validated.config)); // 5. 路径规范化 normalizeConfigPaths(cfg); return cfg;}设计目标
支持大型部署的配置拆分,实现关注点分离。
// openclaw.json{ "$include": [ "./models/anthropic.json5", "./channels/telegram.json5" ], "gateway": { "mode": "remote" }}安全实现
路径遍历防护(includes.ts:198-222):
// 拒绝访问配置目录外的文件if (!isPathInside(this.rootDir, normalized)) {thrownew ConfigIncludeError(`Include path escapes config directory: ${includePath}`,includePath);}// 解析符号链接并重新验证const real = fs.realpathSync(normalized);if (!isPathInside(this.rootRealDir, real)) {thrownew ConfigIncludeError(`Include path resolves outside config directory (symlink)`,includePath);}
合并策略
深度合并算法(includes.ts:70-85):
export function deepMerge(target: unknown, source: unknown): unknown { if (Array.isArray(target) && Array.isArray(source)) { return [...target, ...source]; // 数组连接 } if (isPlainObject(target) && isPlainObject(source)) { const result = { ...target }; for (const key of Object.keys(source)) { result[key] = key in result ? deepMerge(result[key], source[key]) // 递归合并 : source[key]; } return result; } return source; // 原始类型: 后值覆盖前值}多层验证架构
// 验证流水线validateConfigObjectWithPlugins(raw) → validateConfigObjectRaw(raw) // 1. 基础Zod验证 → findLegacyConfigIssues(raw) // 2. 历史配置检查 → findDuplicateAgentDirs(config) // 3. Agent目录冲突检测 → validateIdentityAvatar(config) // 4. Avatar路径安全验证 → validatePluginSchemas(config) // 5. 插件配置验证
Zod Schema 设计
严格模式 + 细粒度校验 (zod-schema.ts):
exportconst OpenClawSchema = z.object({ // 元数据自动时间戳处理 meta: z.object({ lastTouchedAt: z.union([ z.string(), z.number().transform((n, ctx) => { const d = new Date(n); if (Number.isNaN(d.getTime())) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "Invalid timestamp" }); return z.NEVER; } return d.toISOString(); }) ]).optional() }).strict().optional(), // Gateway配置 gateway: z.object({ mode: z.union([z.literal("local"), z.literal("remote")]).optional(), auth: z.object({ mode: z.union([ z.literal("none"), z.literal("token"), z.literal("password"), z.literal("trusted-proxy") ]).optional(), token: z.string().optional().register(sensitive) // 敏感值标记 }).strict().optional() }).strict().optional(), // 跨字段验证}).superRefine((cfg, ctx) => { // 验证broadcast中的agentId是否存在于agents.list const agentIds = new Set(cfg.agents?.list?.map(a => a.id) ?? []); for (const [peerId, ids] of Object.entries(cfg.broadcast ?? {})) { for (const agentId of ids) { if (!agentIds.has(agentId)) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["broadcast", peerId], message: `Unknown agent id "${agentId}"` }); } } }});插件配置验证
动态Schema加载 (validation.ts:418-438):
for (const record of registry.plugins) { const pluginId = record.id; const entry = normalizedPlugins.entries[pluginId]; if (record.configSchema) { const res = validateJsonSchemaValue({ schema: record.configSchema, cacheKey: record.schemaCacheKey ?? pluginId, value: entry?.config ?? {} }); if (!res.ok) { issues.push({ path: `plugins.entries.${pluginId}.config`, message: `invalid config: ${res.errors.join(", ")}` }); } }}双阶段替换
读取时替换(io.ts:652-666):
function resolveConfigForRead(resolvedIncludes, env){ // 1. 应用config.env到process.env applyConfigEnvVars(resolvedIncludes, env); // 2. 替换${VAR}引用 return { resolvedConfigRaw: resolveConfigEnvVars(resolvedIncludes, env), envSnapshotForRestore: { ...env } // 保存快照用于回写 };}写入时恢复:
// 写入前恢复原始的${VAR}引用cfgToWrite = restoreEnvVarRefs( cfgToWrite, parsedRes.parsed, // 原始文件内容 envForRestore // 读取时的环境变量快照);设计原则
不持久化默认值 - 只在加载时应用,写入时剥离。
// 加载时应用默认值const cfg = applyModelDefaults(applyAgentDefaults(applySessionDefaults(validated.config)));// 写入时不包含运行时默认值const stampedOutputConfig = stampConfigVersion(outputConfig);// 不调用 applyModelDefaults
关键默认值
Model 默认值(defaults.ts:213-347):
Agent 默认值(defaults.ts:349-388):
Session 默认值(defaults.ts:144-168):
配置缓存
短期缓存策略(io.ts:1292-1374):
const DEFAULT_CONFIG_CACHE_MS = 200; // 200ms TTLexport function loadConfig(): OpenClawConfig {// 检查运行时快照if (runtimeConfigSnapshot) {return runtimeConfigSnapshot;}// 检查缓存const cached = configCache;if (cached && cached.configPath === configPath && cached.expiresAt > now) {return cached.config;}// 加载并更新缓存const config = io.loadConfig();configCache = {configPath,expiresAt: now + cacheMs,config};return config;}
禁用缓存: OPENCLAW_DISABLE_CONFIG_CACHE=1
写入审计
安全审计日志 (io.ts:1178-1228):
const auditRecord = {ts: new Date().toISOString(),event: "config.write",result: "rename" | "copy-fallback" | "failed",configPath,previousHash,nextHash,previousBytes,nextBytes,changedPathCount,suspicious: ["size-drop", // 大小骤降50%+"missing-meta-before-write","gateway-mode-removed"],pid, ppid, cwd, argv};await appendConfigWriteAuditRecord(deps, auditRecord);
友好错误消息
DM策略错误提示 (io.ts:148-167):
function formatConfigValidationFailure(pathLabel, issueMessage){ const match = issueMessage.match(OPEN_DM_POLICY_ALLOW_FROM_RE); if (!match) return `Config validation failed: ${pathLabel}: ${issueMessage}`; return [ `Configuration mismatch: ${policyPath} is "open", but ${allowPath} does not include "*".`, "", "Fix with:", ` openclaw config set ${allowPath} '["*"]'`, "", "Or switch policy:", ` openclaw config set ${policyPath} "pairing"` ].join("\n");}历史配置检测
迁移警告 (legacy.ts):
export function findLegacyConfigIssues(raw: unknown): LegacyConfigIssue[] { const issues: LegacyConfigIssue[] = []; // 检测已移除的配置项 if (raw.routing?.allowFrom) { issues.push({ path: "routing.allowFrom", message: "routing.allowFrom is removed. Use routing.channels.*.dm.policy instead." }); } // 检测废弃的命名 if (raw.gateway?.token) { issues.push({ path: "gateway.token", message: 'Use "gateway.auth.token" instead.' }); } return issues;}原型污染防护
Blocked Keys(prototype-keys.ts):
exportconst BLOCKED_OBJECT_KEYS = new Set(["__proto__","constructor","prototype"]);export function isBlockedObjectKey(key: string): boolean {return BLOCKED_OBJECT_KEYS.has(key);}
敏感值处理
Zod 敏感值标记(zod-schema.sensitive.ts):
exportconst sensitive = Symbol("sensitive");export function redactSensitiveValues(config: unknown): unknown {// 递归遍历配置对象// 替换标记为sensitive的值为"[REDACTED]"}
文件权限
安全默认值(io.ts:1236-1240):
// 目录权限: 0o700 (仅所有者可访问)await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });// 文件权限: 0o600 (仅所有者可读写)await fs.promises.writeFile(tmp, json, { encoding: "utf-8", mode: 0o600 });
插件配置 Schema
动态加载(plugins/config-schema.ts)
export interface PluginConfigSchema { id: string; kind: "channel" | "memory" | "tool" | "skill"; configSchema?: JSONSchema; channels?: string[];}运行时覆盖
测试与诊断场景(runtime-overrides.ts):
export function applyConfigOverrides(config: OpenClawConfig): OpenClawConfig { // 允许通过环境变量覆盖特定配置项 // 例如: OPENCLAW_OVERRIDE_gateway_mode=local return config;}四、总结展望
2025 年个人效率工具在编程、办公领域得到了极大的发展与提升,2026 年作为企业智能元年,可以预见会出来更多的企业级智能体,这些智能体将朝着更加分布式、安全可控且深度集成业务流程的方向演进。
4.1 架构方向演进
1. 控制平面与执行节点的解耦 (Decoupling Control & Execution)
企业架构将采用类似的“中枢管理 + 边缘执行”模式。总部部署中央控制平面负责权限审核、配置同步和任务路由,而执行节点则部署在员工终端、内部服务器或特定硬件设备上,确保数据在企业内网中闭环处理。
2. 多智能体协作网络 (Multi-Agent Networking)
企业内部将不再是单一的“万能助手”,而是由多个专业化智能体(如 HR 助手、财务助手、IT 运维助手)构成的网络。它们通过标准化的 RPC 协议或内部会话协议进行信息共享和任务接力,形成复杂的自动化流水线。
3. 软硬件深层权限管理 (Hardware & Permission Integration)
企业级 Agent 将深入集成到办公硬件(如会议室系统、扫码枪、PDA)。架构设计上将包含更精细的 TCC(透明度、同意和控制)策略,确保 Agent 在执行敏感操作(如调用公司摄像头或执行系统脚本)时受到严格的合规性审计。
4.2 应用方向演进
1. 全渠道业务渗透 (Omnichannel Business Integration)
企业 Agent 将无缝嵌入到所有内部协作工具中,打破“信息孤岛”。Agent 不仅能回答问题,还能直接在通讯软件中通过 Discord/Slack 动作或自动触发工作流来处理审批、请假或订单更新,实现“对话即办公”。
2. 动态可视化协同工作区 (Live Visual Collaboration)
在复杂的企业决策场景(如供应链管理或数据分析)中,Agent 将利用类似 Canvas 的技术提供动态可视化面板。团队成员可以与 Agent 在同一画布上进行交互式修改,Agent 实时生成数据图表或拓扑图,大幅提升决策效率。
3. 基于沙箱的安全生产环境 (Sandboxed Production Environment)
企业将普遍采用“隔离运行环境”。当 Agent 处理外部客户询价或不受信任的输入时,系统会自动将其放入临时的、受限的容器中执行,防止恶意脚本通过 Agent 渗透进企业核心数据库或内部网络。
4. 企业级“技能中心” (Enterprise Skill Hubs)
企业将建立私有的技能注册表。员工可以为自己的 Agent 订购经 IT 部门认证的“技能包”(如 ERP 插件、专业财务计算器等)。Agent 会根据当前任务上下文,自动从企业私有云拉取并挂载这些技能,实现功能的动态扩展。
未来的企业级智能体将借鉴 OpenClaw 的 Local-first(数据私有)和 Gateway/Node(控制与执行分离)理念,在保证数据所有权(Own your data)的前提下,通过多智能体协作和深度的软硬件集成,成为企业数字化转型的核心基础设施
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2026-03-26
Harness 驾驭工程是 AI 平权的必经之路?
2026-03-26
阿里云 Tablestore 基于 Mem0 为 OpenClaw 构建记忆系统最佳实践
2026-03-26
OpenClaw 自动出 PRD:从选词到产品文档一天搞定
2026-03-26
只需一个指令,让 OpenClaw 安排 TRAE 干活
2026-03-26
OpenClaw 2026.3.24 更新速览:Teams 重大升级 + 技能安装优化 + 20+ 稳定性修复
2026-03-26
AIOps探索:用OpenClaw做AIOps,最大绊脚石不是能力而是成本
2026-03-26
OpenClaw 安装前奏:WSL2 深度调优指南
2026-03-25
OpenClaw 配置备份指南:守护你的数字助手记忆
2026-03-05
2026-02-17
2026-03-03
2026-02-06
2026-02-03
2026-02-16
2026-02-10
2026-03-09
2026-03-09
2026-02-06
2026-03-26
2026-03-24
2026-03-24
2026-03-23
2026-03-21
2026-03-20
2026-03-16
2026-03-14