Apache Maka 运行时沙箱边界:平台选择与命令转换机制全解析
【免费下载链接】makaApache Maka (Incubating) is a high-performance agent workspace that keeps a complete record of everything it did.项目地址: https://gitcode.com/GitHub_Trending/mak/maka
导读
本篇文章聚焦 Apache Maka(Incubating)运行时沙箱边界(Runtime sandbox boundary)模块,即packages/runtime/src/sandbox目录所负责的「平台沙箱选择与命令转换」能力。该模块把会话中活跃的ExecutionBoundary权限配置文件翻译成真实的执行请求,并委派给 macOS Seatbelt、Linux bubblewrap、Windows AppContainer 三大平台后端。读完本文,你将掌握 Maka 沙箱的职责划分、PermissionProfile配置模型、SandboxManager的auto/require/forbid选择语义、各平台后端的实现路径与 fail-closed(故障关闭)策略,以及项目提供的完整验证测试矩阵。
模块定位:只做翻译,不做决策,不亲自动手
packages/runtime/src/sandbox/README.md对模块的边界给出了极其明确的定义:
本目录负责平台沙箱选择与命令转换。它将活跃会话
ExecutionBoundary中的 profile 翻译为执行请求;它不决定请求的边界扩展是否被批准,也不亲自执行该请求。
这句话拆解出三层职责约束:
- 翻译(transform):把权限配置文件转换为可执行的沙箱命令;
- 不审批(no approval):边界扩展是否被允许由 sandbox-boundary 交互路径负责,沙箱选择不会自行扩展边界;
- 不执行(no spawn):
SandboxManager只输出转换后的执行请求,进程的拉起、重试、UI 与遥测都由调用方负责。
代码与聚焦测试是最终权威(Code and focused tests are the final authority)。Windows 侧的强制工作跟踪在 issue #2142,规范文档为 Windows sandbox backend RFC(中文版)。
所有权划分:core 说「是什么」,runtime 说「怎么落地」
沙箱边界语言被刻意拆成两层,两个包各司其职。
@maka/core:平台中立的边界语言
| 模块 | 职责 |
|---|---|
execution-boundary.ts | 定义会话边界、其修订版本号与单调扩展(monotonic expansion) |
permission-profile.ts | 定义 managed / disabled / external 三类 profile、文件系统条目、网络策略、标准 profile 与纯路径匹配器 |
permission-profile-compiler.ts | 当遗留产品模式必须映射到 profile 时保持兼容性 |
这一层刻意保持「纯」(pure):调用方传入归一化绝对路径与运行期上下文,realpath、symlink 与平台路径预处理由 runtime 完成后再进入这些辅助函数(见 permission-profile.ts 的模块注释)。
@maka/runtime:平台转换
| 模块 | 职责 |
|---|---|
types.ts | 定义沙箱选择、命令、路径上下文、执行请求与类型化失败契约 |
sandbox-manager.ts | 判定 profile 是否要求沙箱、选择平台后端并委派转换 |
macos-seatbelt.ts | 构建 Seatbelt 策略,用/usr/bin/sandbox-exec包裹内层 argv |
linux-sandbox.ts | 构建 bubblewrap 挂载、namespace 参数与网络 seccomp 过滤器 |
linux-capability.ts | 在选择可用前探测 bubblewrap 与 namespace 的可用性 |
windows-profile.ts | 把 managed profile 编译为规范的 ACL、网络与环境策略 |
windows-sandbox.ts | 写入一次性 manifest 并调用打包的 AppContainer broker |
default-sandbox-manager.ts | 注册受支持的默认后端 |
index.ts | 公共子路径表面,runtime 包 barrel 再导出受支持的 API |
PermissionProfile模型:受限、无限制与外部三类
PermissionProfile是沙箱决策的输入核心,定义在 permission-profile.ts,为三种类型的联合:
export type PermissionProfile = | PermissionProfileManaged // type: 'managed' | PermissionProfileDisabled // type: 'disabled' | PermissionProfileExternal; // type: 'external'- managed:携带
fileSystem(文件系统策略)与network(网络策略)两个字段; - disabled:仅携带名字,表示权限机制被禁用;
- external:文件系统隔离由外部环境注入(如外部 workspace executor),Maka 在当前实现中不叠加本地平台沙箱。
文件系统策略
文件系统沙箱种类只有三种(FILE_SYSTEM_SANDBOX_KINDS):restricted(受限)、unrestricted(无限制)、external_sandbox(外部沙箱)。
条目(entry)支持两种形态(FileSystemSandboxEntry):
{ kind: 'path', access, path, match? }:访问模式为read/write/deny,路径匹配方式match可取exact(精确)或subtree(子树,向后兼容的默认值);{ kind: 'special', access, special }:特殊路径占位符,取值包括:root、:workspace_roots、:tmpdir、:slash_tmp、:minimal(FILE_SYSTEM_SPECIAL_PATHS),由匹配上下文PermissionProfileMatchContext在运行期解析为真实路径。
此外还有受保护元数据策略(PROTECTED_METADATA_NAMES):默认保护.git、.agents、.codex三个名字,策略为deny_write,防止 Agent 写入仓库元数据目录。
网络策略
网络沙箱种类只有两种(NETWORK_SANDBOX_KINDS):restricted(受限)与enabled(启用)。
四个标准 profile 工厂
permission-profile.ts提供了四个开箱即用的工厂函数,是ask、explore等会话入口的起点:
| 工厂函数 | name | 文件系统 | 网络 | 说明 |
|---|---|---|---|---|
createReadOnlyPermissionProfile() | read-only | restricted,对:workspace_roots只读 | restricted | Explore 会话的起点 |
createWorkspaceWritePermissionProfile() | workspace-write | restricted,对:workspace_roots、:tmpdir、:slash_tmp可写 | restricted | Ask 会话的起点 |
createDangerFullAccessPermissionProfile() | danger-full-access | unrestricted | enabled | 全量访问 |
createExternalPermissionProfile(network?) | external | 由外部提供 | 默认 restricted | 外部隔离 |
注意:workspace-write允许写工作区根、:tmpdir与:slash_tmp,它不是仅限工作区(workspace-only)的 profile(见 README「Product coverage」一节)。isReadOnlyPermissionProfile()则是从策略推导而非看name判断只读:经过批准的边界扩展即使名字没变,也不再被当作只读(permission-profile.ts)。
路径判定辅助函数
canReadPath(profile, path, context):先查 deny 条目,再查 unrestricted/external 放行,最后按 read/write 匹配;canWritePath(profile, path, context):额外叠加受保护元数据 deny-write 检查;isDeniedPath()/isProtectedMetadataPath():分别处理显式拒绝与受保护目录判定。
Windows 上的路径匹配大小写不敏感(isProtectedMetadataPath对 Windows 盘符根折叠大小写),防止写.GIT\config绕过.gitdeny 规则(permission-profile.ts)。
SandboxManager:选择与转换的核心流程
SandboxManager类(sandbox-manager.ts)持有backends映射(sandboxType -> backend),提供四个关键方法。
shouldSandbox:三种偏好的语义
shouldSandbox(profile, preference = 'auto', platform = process.platform): boolean { if (preference === 'forbid') return false; if (preference === 'require') return true; return profileRequiresSandbox(profile); }偏好SandboxablePreference三选一(types.ts):
auto(默认值DEFAULT_PREFERENCE):由 profile 决定——profileRequiresSandbox只有 managed 且文件系统或网络为restricted时返回 true;require:强制选择平台沙箱;forbid:选择宿主机执行。注意这是内部编排输入,不是审批通过的证明。
selectInitial:平台分派的 fail-closed 逻辑
选择流程严格按平台分派:
darwin:有macos-seatbelt后端则选中,否则返回backend_not_available;linux:有linux后端则选中,否则backend_not_available;win32:有windows后端则选中,否则backend_not_available;- 其他平台:返回
unsupported_platform。
所有失败路径都带requiresSandbox: true与明确的message,即故障关闭:不静默降级为宿主机执行。
canEnforce与transform
canEnforce(input):在热路径上回答「当前平台与 profile 是否真的可被强制」,会依次检查后端已注册、isAvailable、canEnforceProfile;transform(request):先selectInitial,若选中none则原样透传命令(sandboxType: 'none'),否则委派给对应后端的transform。
后端接口契约
SandboxBackend(types.ts)只有三个成员:
export interface SandboxBackend { readonly type: Exclude<SandboxType, 'none'>; isAvailable?(platform?: SandboxPlatform): boolean; canEnforceProfile?(profile: PermissionProfile): boolean; transform(request: SandboxTransformRequest): SandboxTransformResult; }SandboxCommand:program、args、cwd、env、profile、pathContext;SandboxExecRequest:转换产物,含argv、fdInputs、cwd、env、sandboxType、effectiveProfile;SandboxPathContext:承载workspaceRoots、tmpdir、slashTmp、minimalRoots以及运行期辅助目录(runtimeReadableRoots、executableRoots、runtimeWritableRoots),还有通过开放文件描述符钉住的pinnedRuntimeWritableRoots/pinnedProfilePaths——这些是「沙箱启动前用宿主 fd 钉住运行期可写根」的机制。
类型化失败原因SandboxTransformFailureReason覆盖五种:unsupported_platform、backend_not_available、backend_not_implemented、sandbox_required、invalid_request。
三大平台后端
macOS:Seatbelt + sandbox-exec
MacosSeatbeltBackend构建 SBPL(Seatbelt policy language)策略,并用/usr/bin/sandbox-exec包裹内层 argv。职责包括 SBPL 生成、根参数化(root parameterization)、受保护元数据 deny-write 规则与网络策略翻译。当后端不可用时 fail closed。
index.ts导出了MACOS_SEATBELT_BASE_POLICY、MACOS_SEATBELT_EXECUTABLE、MACOS_SEATBELT_PLATFORM_DEFAULTS_POLICY、buildSeatbeltPolicy、createSeatbeltExecArgs、escapeSeatbeltRegex等构建工具(index.ts)。
Linux:bubblewrap + seccomp
LinuxBubblewrapBackend构建 bubblewrap 挂载、namespace 参数与网络 seccomp 过滤器。可用性由linux-capability.ts负责:通过detectLinuxSandboxCapability探测 bubblewrap 可执行文件、namespace 能力;LINUX_BWRAP_PROBE_ARGS与LINUX_BWRAP_REQUIRED_OPTIONS定义了探测参数与必需选项。当可执行文件、namespace 探测或请求的 profile 无法被强制时 fail closed。
Windows:AppContainer broker(预览)
WindowsBrokerSandboxBackend写入一次性 manifest 并调用打包的 AppContainer broker(maka-windows-sandbox.exe)。仅当打包的原生资源存在时才被选中,否则 fail closed 为不可用。windows-profile.ts的compileWindowsSandboxPolicy把 managed profile 编译为规范 ACL、网络与环境策略。
Windows 可用性判定有一套精心设计的 readiness 机制(default-sandbox-manager.ts):
probeWindowsReadiness:运行 launcher 的--readiness-probe,真实拉起生产身份与一次性受限子进程,超时上限 15 秒;只有干净退出码 0 才算可用;- 探测结果按 launcher 路径做模块级缓存:正结果永久缓存,负结果仅缓存 60 秒(
WINDOWS_READINESS_NEGATIVE_TTL_MS),避免一次瞬时抖动(杀软扫描、负载下的 spawn 超时)毒化整个进程生命周期的可用性; readCachedWindowsReadiness:热路径上只读缓存、绝不 spawn,避免spawnSync阻塞事件循环;恢复被限定在下次 composition 构建或 Runtime Host 重启(对应 RFC §6.4)。
当前行为与产品覆盖矩阵
当前行为要点
- 受限的 managed profile 在默认
auto偏好下必须有平台沙箱; - unrestricted、disabled 与 external profile 不叠加 Maka 管理的本地沙箱;
require强制平台沙箱选择;forbid选择宿主机执行,且只是内部编排输入,不是审批证明;- macOS 选 Seatbelt,后端不可用时 fail closed;
- Linux 选 bubblewrap,可执行文件、namespace 探测或 profile 无法强制时 fail closed;
- Windows 仅在打包原生资源存在时选 AppContainer broker,否则 fail closed 为不可用;其他平台返回
unsupported_platform; - 后端收到无效或不支持的 profile 返回类型化失败,不静默降级为宿主机执行。
产品覆盖矩阵(README 原文继承)
| Surface | macOS | Linux | Windows | 当不需要 Maka 托管沙箱时 |
|---|---|---|---|---|
| Agent Bash,前台或后台且无 PTY | Seatbelt | bubblewrap | 受限 managed 执行 fail closed——AppContainer broker 无法启动任意 shell | 通过探测到的宿主 shell 运行 |
| 带 PTY 的 Agent Bash | 当活跃 profile 要求沙箱时拒绝 | 当活跃 profile 要求沙箱时拒绝 | 当活跃 profile 要求沙箱时拒绝 | 作为宿主 PTY 运行 |
本地路径Read、Write、Edit、FormatJson、Glob、Grep、apply_patch | Seatbelt 下的文件系统 worker | bubblewrap 下的文件系统 worker | AppContainer broker 下特制的文件系统 worker,受下述 fail-closed 限制约束 | managed 执行使用无 OS 沙箱的 worker;bypass 使用宿主本地 executor;external 使用注入的 executor |
对 runtime 或 attachment 资源引用的Read | 资源服务;非本地文件系统 worker 操作 | 资源服务;非本地文件系统 worker 操作 | 资源服务;非本地文件系统 worker 操作 | 相同的资源服务路径 |
客户端runtime.resource.start集成终端 | 托管 Agent 边界之外的宿主 PTY | 托管 Agent 边界之外的宿主 PTY | 托管 Agent 边界之外的宿主 PTY | 相同的宿主 PTY 路径 |
Windows AppContainer 预览的已知限制
在 Windows AppContainer 预览中,本地路径Read、Glob、对已存在目标的Write、Edit、FormatJson、apply_patch更新操作走文件系统 worker;Grep以grep_unavailablefail closed;对缺失目标的Write与apply_patch创建/删除操作同样 fail closed——因为当前 broker 策略无法在不加宽内核授权的前提下表达「对父条目的精确写权限」。
会话起点与绕过边界
ask从 managedworkspace-writeprofile 起步,explore从 managed 只读 profile 起步,两者都因文件系统或网络策略受限而要求平台沙箱;- bypass 边界、unrestricted managed profile 与 disabled profile 不请求 Maka 托管本地沙箱;
- 当文件系统 worker 已接入时,即使
SandboxManager选择了none,managed 执行仍可用 worker 作为后端——这是进程分离,而非 OS 沙箱强制; - bypass 边界用宿主本地 executor;external 边界把文件系统隔离委托给注入的 workspace executor,不叠加本地平台沙箱;
- 不需要沙箱时工具可用性与权限策略依然生效:选择
none本身不是执行许可。
边界与非目标
边界(Boundaries)
- 会话
ExecutionBoundary是「操作当前是否位于沙箱边界内」的权威;沙箱选择不会扩展该边界; - sandbox-boundary 交互路径拥有用户审批权,并以新修订号原子落定已批准的扩展;
- 调用方负责规范 cwd 与路径上下文构造,平台后端不得猜测工作区根;
SandboxManager只转换命令:不 spawn 进程、不无沙箱重试、不弹 UI、不持有遥测;- macOS 后端拥有 SBPL 生成、根参数化、受保护元数据 deny-write 规则与网络策略翻译;
PermissionProfile.External表示文件系统隔离由环境提供,Maka 当前实现不叠加本地平台沙箱。
非目标(Non-goals)
- 工作树或工作区副本沙箱化
- Diff/回写或 apply-patch UI
- 自动无沙箱重试
- 托管网络代理或域名白名单
- Windows 发布签名与完整 Phase 4 对抗支持声明
- 第二套权限语言、shell runner 或文件策略系统
默认后端注册与公开 API
createDefaultSandboxManager()注册MacosSeatbeltBackend与LinuxBubblewrapBackend;createBuiltinSandboxManager()在检测到打包的 Windows launcher 时额外追加WindowsBrokerSandboxBackend(default-sandbox-manager.ts)。isBuiltinFilesystemWorkerSandboxAvailable()是文件系统 worker 可用性的单一权威来源:macOS 恒为 true,Windows 依赖 launcher 存在 + readiness 探测,Linux 依赖 bubblewrap capability 探测(default-sandbox-manager.ts)。
公共子路径表面由 index.ts 导出,主要包括:SandboxManager、createBuiltinSandboxManager/createDefaultSandboxManager/isBuiltinFilesystemWorkerSandboxAvailable、LinuxBubblewrapBackend(含buildBubblewrapArgv、buildNetworkSeccompFilter)、MacosSeatbeltBackend(含buildSeatbeltPolicy、createSeatbeltExecArgs)、WindowsBrokerSandboxBackend、compileWindowsSandboxPolicy、classifyWindowsBrokerFailure以及全部类型契约(SandboxBackend、SandboxCommand、SandboxExecRequest、SandboxTransformResult等),并通过 runtime 包 barrel 对外再导出。
验证:代码与测试是最终权威
README「Verification」一节给出了完整的测试映射,全部可以直接在仓库中复现:
core 层
- Profile 工厂、编译器与匹配器:
packages/core/src/__tests__/permission-profile*.test.ts
runtime 层选择与转换
- 选择与转换:
packages/runtime/src/__tests__/sandbox-manager.test.ts
macOS
- 策略与包裹:
packages/runtime/src/__tests__/macos-seatbelt.test.ts - 平台行为:
packages/runtime/src/__tests__/macos-seatbelt-smoke.test.ts - 文件系统 worker 行为:
packages/runtime/src/__tests__/filesystem-worker-smoke.test.ts
Linux
- 策略与包裹:
packages/runtime/src/__tests__/linux-sandbox.test.ts - 平台行为:
packages/runtime/src/__tests__/linux-sandbox-smoke.test.ts - 文件系统 worker 行为:
packages/runtime/src/__tests__/filesystem-worker-linux-smoke.test.ts
Windows
- profile 与 broker 转换:
windows-profile.test.ts与windows-sandbox.test.ts - 文件系统 worker 行为:
packages/runtime/src/__tests__/filesystem-worker-windows-smoke.test.ts
产品组合与导出
- Runtime Host 产品组合:
packages/runtime-host/src/__tests__/execution-model-composition.test.ts - 公共导出与默认注册:
sandbox-export.test.ts与default-sandbox-manager.test.ts
与边界语言配套的还有packages/core/src/__tests__/sandbox-boundary.test.ts与packages/runtime/src/__tests__/tool-runtime-sandbox-boundary.test.ts等测试,覆盖边界扩展审批与工具运行期边界校验;packages/runtime/src/__tests__/execution-boundary-test-helpers.ts则为各测试提供构造 ExecutionBoundary 的共享工具。
小结
Maka 的运行时沙箱边界是一个「纯翻译层」:core 定义平台中立的权限语言,runtime 按平台选择后端并把命令转换为带沙箱的执行请求,任何无法强制的情况都以类型化失败 fail closed,绝不静默降级。理解PermissionProfile三类模型、auto/require/forbid选择语义与三个平台后端的可用性探测,是正确使用和扩展 Maka 沙箱能力的基础;而 README 与上述测试文件共同构成该模块「代码即文档」的可验证事实来源。
【免费下载链接】makaApache Maka (Incubating) is a high-performance agent workspace that keeps a complete record of everything it did.项目地址: https://gitcode.com/GitHub_Trending/mak/maka
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考