Agent Harness 学习
← 返回对照矩阵

sandbox & permission

运行时

沙箱与权限

模型要执行一条 shell 命令,什么东西挡在它和操作系统之间?

横向读这一行

分成两个层次而不是四种方案:Codex 与 DeepSeek 真正下沉到操作系统(seatbelt / landlock / bwrap / Windows ACL),openJiuwen 与 Pi 停在策略层(规则引擎 / 目录信任)。更有意思的是 Codex 与 DeepSeek 用了完全相同的三个模式名 read-only / workspace-write / danger-full-access,但一个把它定义成协议里的类型枚举,另一个把它定义成会话事件日志的折叠结果。

对比
openJiuwen带代码

策略层规则引擎,三档判定 allow / ask / deny

没有操作系统级沙箱(仓库内检索不到 seatbelt / landlock / bwrap)。防线是一套分级权限引擎:内置 YAML 规则库、shell AST 解析(shell_ast.py)、路径匹配器与参数规则命中,最终把每次工具调用归结为 ALLOW / ASK / DENY 三档,并返回命中的规则名与理由。多条规则命中时取最严格的一档。

class PermissionLevel(str, Enum):
    """权限级别.

    - ALLOW: 直接执行,无需确认
    - ASK:   弹出确认框,用户决定
    - DENY:  拒绝执行,返回错误
    """
    ALLOW = "allow"
    ASK = "ask"
    DENY = "deny"


@dataclass
class PermissionResult:
    permission: PermissionLevel
    matched_rule: str | None = None
    reason: str | None = None
openjiuwen/harness/security/models.py:19-35@ fd6c47854201
Codex带代码

协议里的类型枚举,落到 seatbelt / landlock / Windows

沙箱模式是协议层的一个带数据的枚举,序列化为 kebab-case 传给所有前端。它不是描述性标签——codex-rs/sandboxing 下有 seatbelt(macOS,含 .sbpl 策略文件)、landlock 与 bwrap(Linux)、windows.rs 的真实实现,以及独立的 execpolicy 与 shell-escalation。注意 WorkspaceWrite 分支携带 writable_roots 和 network_access:权限边界是有结构的数据,不是一个布尔开关。

pub enum SandboxPolicy {
    /// No restrictions whatsoever. Use with caution.
    #[serde(rename = "danger-full-access")]
    DangerFullAccess,

    /// Read-only access configuration.
    #[serde(rename = "read-only")]
    ReadOnly {
        /// When set to `true`, outbound network access is allowed. `false` by
        /// default.
        network_access: bool,
    },

    /// Same as `ReadOnly` but additionally grants write access to the current
    /// working directory ("workspace").
    #[serde(rename = "workspace-write")]
    WorkspaceWrite {
        writable_roots: Vec<AbsolutePathBuf>,
        network_access: bool,
        // ...
    },
codex-rs/protocol/src/protocol.rs:1010-1050@ 4beea50e26dd

同样三个模式名,但状态是会话事件的折叠结果

模式名与 Codex 逐字相同,实现哲学却相反:切换沙箱模式不写配置文件,而是往会话日志里追加一条 sandbox/mode 事件;当前生效值等于「折叠所有事件的结果 ?? 部署默认值」。源码注释点破了这样做的收益——重启后靠重放日志恢复,不需要任何追赶机制;两个会话永远看不到对方的状态;也就不存在外部配置存储。这是「事件日志即状态」用在权限上的一个样本。

/** Every {@link SandboxMode}, for option advertisement and runtime validation of untrusted mode strings. */
export const SANDBOX_MODES: readonly SandboxMode[] = ['read-only', 'workspace-write', 'danger-full-access']

// —— 同文件顶部模块注释节选 ——
// A runtime switch is recorded as one `sandbox/mode` event on the session it
// applies to; effective = fold(events) ?? the deployment default, so an
// override survives restart by replay, two sessions can never see each other's
// state, and there is no external config store.
packages/sandbox/sandbox-policy/src/session-mode.ts:41-42@ b150a551b8d4
Pi带代码

不做沙箱,改问「你信任这个目录吗」

内核里没有沙箱:OS 级隔离只以示例扩展形式存在于 examples/extensions/sandbox/,bash-executor.ts 全文检索不到 sandbox 字样。取而代之的是目录级信任——首次进入一个项目目录时弹出确认,用户授权后才加载该目录的配置、安装缺失依赖、执行项目扩展。这是把隔离责任显式交还给用户和运行环境(容器、CI)的选择,与其「内核要小到能读懂」的主张一致。

function formatProjectTrustPrompt(cwd: string): string {
	return `Trust project folder?\n${cwd}\n\nThis allows ${APP_NAME} to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, and execute project extensions.`;
}
packages/coding-agent/src/core/project-trust.ts:24-26@ bfb004d4418f

design space / 设计空间分析

codex 和 deepseek harness 用的是同一套三档模式名,但格子显示出的是完全不同的实现哲学;openJiuwen 和 pi 都没有内核级 OS 沙箱,却分别选择了「建规则引擎」和「问用户信不信」两条相反的路。

openJiuwen 没有 OS 级沙箱(格子说明仓库内检索不到 seatbelt / landlock / bwrap),防线是一套策略层规则引擎:内置 YAML 规则库、shell AST 解析(shell_ast.py)、路径匹配器与参数规则命中,最终把每次工具调用归结为 PermissionLevel 的 ALLOW / ASK / DENY 三档,PermissionResult 带 matched_rule 和 reason,多条规则命中时取最严格的一档。

codex 的沙箱模式是协议层的带数据枚举 SandboxPolicy,序列化为 kebab-case 传给所有前端,不是描述性标签——codex-rs/sandboxing 下有 seatbelt(macOS,含 .sbpl 策略文件)、landlock 与 bwrap(Linux)、windows.rs 的真实实现,另有独立的 execpolicy 与 shell-escalation。WorkspaceWrite 分支携带 writable_roots 和 network_access 两个字段,权限边界是结构化数据,不是一个布尔开关。

deepseek harness 的 SANDBOX_MODES 是 ['read-only', 'workspace-write', 'danger-full-access'],与 codex 的三个模式名逐字相同,但状态模型是反过来的:切换沙箱模式不写配置文件,而是往会话日志追加一条 sandbox/mode 事件;当前生效值等于 fold(events) ?? 部署默认值。源码注释点出这样做的收益:重启后靠重放日志恢复,不需要追赶机制;两个会话永远看不到对方的状态;也就不存在外部配置存储。

pi 的内核里同样没有沙箱:OS 级隔离只以示例扩展形式存在于 examples/extensions/sandbox/,bash-executor.ts 全文检索不到 sandbox 字样。取而代之的是目录级信任:formatProjectTrustPrompt 在首次进入项目目录时弹出确认,用户授权后才加载该目录配置、安装缺失依赖、执行项目扩展——隔离责任被显式交还给用户和运行环境。

编辑判断

codex 和 deepseek harness 共享同一套模式命名,容易让人误以为两家的沙箱实现是同一件事,但格子里能看到的落地方式完全不同:codex 的三档背后是 seatbelt / landlock / bwrap / windows.rs 这些真实的操作系统级实现,deepseek harness 的三档背后是一套事件日志折叠出来的状态值,格子本身没有写明这个状态值最终如何在操作系统层面被强制执行。名字相同不代表防线的位置相同,这是本站的编辑提醒。另外,openJiuwen 与 pi 都没有内核级 OS 沙箱,但选择的补偿方式相反——前者建一整套规则引擎自己判定,后者把判断交还给用户的一次性信任决定——这也是编辑判断,不是任一项目的官方表述。

尚未收敛:deepseek harness 的 sandbox/mode 事件折叠机制只说明了「模式值如何被记录和读取」,格子本身没有说明这个值最终靠什么去约束进程能不能读写文件或联网——是否存在与 codex 的 seatbelt/landlock 对等的强制执行层,本站目前没有读到对应源码,不能假定两者在「强制执行」这一步是等价的。

back to course / 回到课程

矩阵展示的是「各家怎么做」。这个问题本身为什么存在、有哪些经典权衡,在课程里讲:

8

运行时、可靠性与安全

生产级 Agent:幂等、隔离、身份与网关