在本文中,我们将讨论新近热门的开源项目 Anthropic 的 sandbox-runtime (srt),即 Claude Code 沙箱化 bash 工具背后的操作系统级沙箱。Anthropic sandbox-runtime 限制进程可以触碰哪些文件、可以访问哪些主机。它无需启动容器即可做到这一点。它也不与 Claude Code 绑定。你自己的 agent、本地 MCP 服务器以及普通命令行程序都可以在其内部运行。
TL;DR
- 大小: npm 包
@anthropic-ai/sandbox-runtimev0.0.81,解压后约 11.4 MB,Apache-2.0 许可,需要 Node.js 22.12+。 - 运行环境: 任何 macOS 或 Linux 开发机或 CI 机器。macOS 使用 Seatbelt;Linux 需要 bubblewrap、socat 和 ripgrep。Windows 为 alpha 版。
- 性能: 据 Anthropic 内部使用情况(自述)统计,沙箱化使 Claude Code 的权限提示减少了 84%。
- 优点: 权限提示减少 84%,并对整个进程树强制执行文件与网络限制。
- 缺点: 仍处于 beta 研究预览阶段,Windows 支持为 alpha 版。
什么是 Anthropic sandbox-runtime?
Anthropic sandbox-runtime 是一个开源工具,可将任何进程包裹在文件系统与网络限制之中。你可以用 npm install -g @anthropic-ai/sandbox-runtime安装它,然后在命令前加上 srt前缀。它既可以作为 CLI 使用,也可以作为 TypeScript 库使用。
sandbox-runtime 在 macOS、Linux 和 Windows 上是如何工作的?
srt 使用各操作系统自身的隔离原语。其限制覆盖整个进程树,包括脚本和子进程。
- macOS:
sandbox-exec使用动态生成的 Seatbelt 配置文件。只需要 ripgrep。 - Linux: bubblewrap 并移除网络命名空间。它还需要 socat 和 ripgrep。
- Windows(alpha): 使用专用
srt-sandbox账户、Windows Filtering Platform 出站围栏以及显式文件 ACL。
流量仅通过主机上运行的 HTTP 和 SOCKS5 代理输出。在 Linux 上,这些代理位于 bind 挂载进沙箱的 Unix 套接字之后。在 macOS 上,Seatbelt 配置文件只允许一个本地主机端口。然后,代理会应用你的域名允许列表和拒绝列表。
为什么既需要文件系统隔离又需要网络隔离?
Anthropic 团队认为,只设 1 种边界而缺另一种是会失效的。没有网络隔离,被提示注入的代理可能泄露 SSH 密钥。没有文件系统隔离,它可能逃出沙箱并获得网络访问权限。
srt 的默认设置遵循这一逻辑。读取操作除非被明确拒绝,否则处处允许。写入操作除非被明确允许,否则处处拒绝。网络访问除非域名被加入允许列表,否则一律拒绝。
如何对 MCP 服务器或你自己的代理进行沙箱隔离?
对于 MCP 服务器,将 command 中的 .mcp.json 从 npx 改为 srt。把原始命令作为参数传递。规则随后保存在 ~/.srt-settings.json:
{
"network": { "allowedDomains": ["api.github.com", "*.npmjs.org"], "deniedDomains": [] },
"filesystem": { "denyRead": ["~/.ssh"], "allowWrite": ["."], "denyWrite": [".env"] }
}
对于你自己的代理,该库暴露了 SandboxManager。调用 initialize(config) 来启动代理。然后 wrapWithSandbox() 会返回一个你可以直接启动的包装后的命令字符串。
84% 这个数字是什么意思?
Anthropic 报告称 ,沙箱隔离在其内部 Claude Code 使用中将权限提示减少了 84%。这是一个自我报告的数字,并非外部基准测试结果。其目标是解决审批疲劳。整天点击“批准”的开发者最终不再细看自己批准的内容。一个固定的边界让代理可以在边界内自由工作,只在边缘处才请求许可。
上一步</button> <button class="btn pri" id="fnext">下一步 </button> <button class="btn" id="fplay">自动播放</button> </div> </div> <div class="pane" id="p-sim"> <div class="sim"> <div> <div class="card"> <h5>network.allowedDomains</h5> <label class="chk"><input type="checkbox" id="d-anth" checked> anthropic.com</label> <label class="chk"><input type="checkbox" id="d-npm"> *.npmjs.org</label> <label class="chk"><input type="checkbox" id="d-gh"> github.com</label> <h5 style="margin-top:12px">filesystem</h5> <label class="chk"><input type="checkbox" id="f-ssh" checked> denyRead: ~/.ssh</label> <label class="chk"><input type="checkbox" id="f-env" checked> denyWrite: .env</label> </div> </div> <div> <div class="cmds" id="cmds"> <button class="cmd" data-c="0">srt "cat README.md"</button> <button class="cmd" data-c="1">srt "cat ~/.ssh/id_rsa"</button> <button class="cmd" data-c="2">srt "curl anthropic.com"</button> <button class="cmd" data-c="3">srt "curl example.com"</button> <button class="cmd" data-c="4">srt "npm install"</button> <button class="cmd" data-c="5">srt "echo KEY=1 >> .env"</button> </div> <div class="term" id="term" style="margin-top:10px"><span class="dim">选择一个命令。切换配置以查看变化。</span></div> <div class="why" id="why"></div> </div> </div> </div> <div class="pane" id="p-plat"> <div class="plat"> <div class="pc on" data-p="0"><h4>macOS</h4><span class="tag t-g">支持</span><ul><li>sandbox-exec</li><li>Seatbelt 配置文件</li><li>需要 ripgrep</li></ul></div> <div class="pc" data-p="1"><h4>Linux</h4><span class="tag t-g">支持</span><ul><li>bubblewrap</li><li>无网络命名空间</li><li>socat + ripgrep</li></ul></div> <div class="pc" data-p="2"><h4>Windows</h4><span class="tag t-o">Alpha</span><ul><li>srt-sandbox 账户</li><li>WFP 出站围栏</li><li>一次性安装</li></ul></div> </div> <div class="desc pdetail" id="pdesc"></div> </div> <div class="pane" id="p-defs"> <div class="defs"> <div class="dc"><div class="kicker">读</div><div class="big" style="color:#b5cc94">允许</div><p>默认在所有位置允许。通过 denyRead 设定 ~/.ssh 等拒绝区域,并可用 allowRead 重新允许。</p></div> <div class="dc"><div class="kicker">写</div><div class="big" style="color:#ef7a73">拒绝</div><p>默认在所有位置拒绝。通过 allowWrite 开放 "." 和 /tmp 等路径。</p></div> <div class="dc"><div class="kicker">网络</div><div class="big" style="color:#ef7a73">拒绝</div><p>默认无访问权限。每个主机都必须匹配 allowedDomains。</p></div> </div> <div class="bars" id="bars"> <div class="kicker" style="margin-bottom:4px">Claude Code 权限提示(已按指数化)</div> <div class="bar"><div class="lb"><span>无沙箱</span><span>100</span></div><div class="bg"><div class="fg" data-w="100" style="background:var(--muted)"></div></div></div> <div class="bar"><div class="lb"><span>有沙箱</span><span>16</span></div><div class="bg"><div class="fg" data-w="16" style="background:var(--orange)"></div></div></div> <div class="note">Anthropic 内部使用中提示减少了 84%(自我报告,2025 年 10 月的工程文章)。以 100 为基准指数化。</div> </div> </div> <div class="pane" id="p-cmp"> <div class="tblwrap"> <table> <thead><tr><th></th><th>srt</th><th>Codex CLI</th><th>Docker Sandboxes</th><th>E2B</th></tr></thead> <tbody> <tr class="row"><td>隔离方式</td><td class="hl">操作系统原语</td><td>操作系统原语</td><td>microVM</td><td>Firecracker microVM</td></tr> <tr class="row"><td>运行位置</td><td class="hl">本地</td><td>本地</td><td>本地</td><td>云 / BYOC</td></tr> <tr class="row"><td>容器或虚拟机</td><td class="hl">否</td><td>否</td><td>是</td><td>是</td></tr> <tr class="row"><td>网络默认值</td><td class="hl">全部拒绝</td><td>关闭(可切换)</td><td>自选策略</td><td>开启</td></tr> <tr class="row"><td>域名允许列表</td><td class="hl">通配符 + 端口</td><td>未公开</td><td>通配符 + 端口</td><td>allowOut、通配符</td></tr> <tr class="row"><td>许可证</td><td class="hl">Apache-2.0</td><td>Apache-2.0</td><td>未公开</td><td>Apache-2.0 (SDK)</td></tr> </tbody> </table> </div> </div> <div class="ft"><span>来源:github.com/anthropics/sandbox-runtime · anthropic.com/engineering · v0.0.81</span><span class="mtp">由 Marktechpost 构建</span></div> </div> <script> (function(){ var R=document.getElementById('srt-x'); function post(){try{parent.postMessage({srtH:R.offsetHeight+40},'*')}catch(e){}} var tabs=R.querySelectorAll('.tab'); tabs.forEach(function(b){b.addEventListener('click',function(){ tabs.forEach(function(x){x.classList.remove('on')});b.classList.add('on'); R.querySelectorAll('.pane').forEach(function(p){p.classList.remove('on')}); var t=b.getAttribute('data-t');document.getElementById('p-'+t).classList.add('on'); if(t==='defs')bars();if(t==='cmp')rows();setTimeout(post,60); })}); // 流程 var steps=[ ['您的代理运行一条命令','代理、MCP 服务器或 CLI 工具发出一条命令。命令本身不变;srt 只是将其简单包装。',0,'act'], ['srt 构建策略','srt 加载 ~/.srt-settings.json(或内置默认值),并在主机上启动 HTTP 和 SOCKS5 代理。',1,'act'], ['操作系统强制执行边界','Seatbelt(macOS)、bubblewrap(Linux)或 WFP 防护(Windows alpha)将文件规则应用于整个进程树。',2,'act'], ['所有流量都经过代理','沙箱没有直接的外出路由。在 Linux 上,网络命名空间被移除;代理通过 Unix 套接字访问。',3,'act'], ['允许的主机:连接继续','allowedDomains 中的主机在通过检查(确认其不会解析到环回或云元数据 IP)后放行。',4,'ok'], ['未知主机:被阻止或询问','被拒绝或未列出的主机将被拒绝连接,或由 ask 回调决定。违规行为会被记录下来供代理读取。',4,'bad'] ]; var fi=0,timer=null; function draw(){ var s=steps[fi];var ns=R.querySelectorAll('.node'); ns.forEach(function(n,i){n.classList.remove('act','ok','bad');if(i<s[2])n.classList.add('act')}); ns[s[2]].classList.add(s[3]); var pk=document.getElementById('pkt');pk.style.left=(s[2]*22)+'%'; pk. style.background=s[3]==='ok'?'#788c5d':s[3]==='bad'?'#d4504a':'#d97757'; document.getElementById('fdesc').innerHTML='<h4>第 '+(fi+1)+' 步,共 '+steps.length+' 步: '+s[0]+'</h4>'+s[1]; } document.getElementById('fnext').onclick=function(){fi=(fi+1)%steps.length;draw()}; document.getElementById('fprev').onclick=function(){fi=(fi-1+steps.length)%steps.length;draw()}; document.getElementById('fplay').onclick=function(){ if(timer){clearInterval(timer);timer=null;this.textContent='自动播放';return} this.textContent='暂停';timer=setInterval(function(){fi=(fi+1)%steps.length;draw()},1800); }; draw(); // 模拟 function on(id){return document.getElementById(id).checked} var C=[ function(){return [1,'# Anthropic Sandbox Runtime (srt) ...','默认允许读取,当前目录是可读的。']}, function(){return on('f-ssh')?[0,'cat: ~/.ssh/id_rsa: 操作不被允许','~/.ssh 在 denyRead 中,因此无法读取该密钥。']:[1,'-----BEGIN OPENSSH PRIVATE KEY----- ...','没有 denyRead 规则时,读取在任何地方都是开放的。请显式拒绝机密文件。']}, function(){return on('d-anth')?[1,'<html>...</html>','anthropic.com 在 allowedDomains 中,因此代理会转发它。']:[0,'连接被网络允许列表阻止','没有匹配允许列表:网络默认拒绝。']}, function(){return [0,'连接被网络允许列表阻止','example.com 不在允许列表中。']}, function(){return on('d-npm')?[1,'软件包已安装(示例输出)','*.npmjs.org 在允许列表中,因此可以访问该注册表。']:[0,'npm 错误!对 registry.npmjs.org 的网络请求失败\n连接被网络允许列表阻止','经典的权衡:过于严格,安装就会失败。']}, function(){return on('f-env')?[0,'bash: .env: 操作不被允许','denyWrite 优先于 allowWrite,因此 .env 在项目内部保持受保护。']:[1,'(已写入)','"." 在 allowWrite 中,因此写入成功。']} ]; var busy=false; R.querySelectorAll('.cmd').forEach(function(b){b. addEventListener('click',function(){ if(busy)return;busy=true;var r=C[+b.getAttribute('data-c')]();var t=document.getElementById('term'); t.innerHTML='<span class="p">$</span> '+b.textContent.replace(/</g,'<')+'\n<span class="dim">正在沙箱内运行...</span>'; document.getElementById('why').textContent=''; setTimeout(function(){ t.innerHTML='<span class="p">$</span> '+b.textContent.replace(/</g,'<')+'\n<span class="'+(r[0]?'ok':'no')+'">'+(r[0]?'✓ ':'✗ ')+r[1].replace(/</g,'<')+'</span>'; document.getElementById('why').textContent=r[2];busy=false;post(); },650); })}); // 平台 var P=['macOS 使用内置的 sandbox-exec 工具,并为每次运行生成 Seatbelt 配置文件。该配置文件只允许 1 个本地主机端口,srt 代理在该端口上监听。违规信息可以从系统日志中实时读取。', 'Linux 使用 bubblewrap 并完全移除网络命名空间。代理通过绑定挂载到沙箱中的 Unix 套接字访问。Ubuntu 24.04+ 需要对 AppArmor 进行更改以支持用户命名空间。', 'Windows(alpha)以专用的 srt-sandbox 账户运行命令。一个全机范围的 WFP 过滤器阻止其出站流量,代理端口除外。设置需要 1 次提权安装。']; var pcs=R.querySelectorAll('.pc'); function pd(i){pcs.forEach(function(x){x.classList.remove('on')});pcs[i].classList.add('on');document.getElementById('pdesc').innerHTML='<h4>'+pcs[i].querySelector('h4').textContent+'</h4>'+P[i];post()} pcs.forEach(function(c,i){c.addEventListener('click',function(){pd(i)})});pd(0); function bars(){R.querySelectorAll('.fg').forEach(function(f){f.style.width='0';setTimeout(function(){f.style.width=f.getAttribute('data-w')+'%'},80)})} function rows(){R.querySelectorAll('tr.row').forEach(function(r,i){r.classList.remove('in');setTimeout(function(){r.classList.add('in')},90*i+60)})} window.addEventListener('load',post);window. addEventListener('resize',post);setTimeout(post,300); })(); </script> </body> </html> ">真正的权衡:严格的白名单 vs 可用的智能体
编码智能体不只是编辑文件。它们会安装依赖项、拉取文档并调用 API。如果把允许列表设得过于严格,那么 npm install 失败。而设置得太宽松,沙箱就起不到多少保护作用。srt 提供了几个旋钮来调到中间的平衡点:
- 通配符和端口: 类似以下的条目
*.npmjs.org或api.example.com:443. - 请求回拨: 决定主机没有覆盖的规则,并可以返回模型能读取的原因。
- 按命令的允许列表: 仅为1次调用打开一个注册表。
- 实时更新:
--control-fd在命令运行期间交换网络列表。 - 解析地址检查: 阻止解析到环回或云元数据 IP 地址的名称。
- 被拒绝域的原因: 告诉代理被认可的替代方案,例如一个 HTTPS 的 git 远程仓库。
它与其他代理沙箱相比如何?
| 功能 | Anthropic sandbox-runtime | OpenAI Codex CLI 沙箱 | Docker 沙箱 | E2B |
|---|---|---|---|---|
| 隔离 | OS 原语:Seatbelt、bubblewrap、Windows 账户 + WFP | OS 原语:Seatbelt、bubblewrap、原生 Windows 沙箱 | 每个沙箱一个 microVM | 每个会话一个 Firecracker microVM |
| 运行位置 | 本地机器 | 本地机器 | 本地机器 | 云或 BYOC(AWS、GCP) |
| 需要容器或虚拟机 | 否 | 否 | 是(microVM) | 是(托管 microVM) |
| 在其自身代理之外使用 | 是,提供适用于任何进程的 CLI + 库 | 内置于 Codex | 是,供编码代理使用的 sbx CLI | 是,JS 和 Python SDK |
| 网络默认设置 | 全部拒绝;代理允许列表 | 在 中默认关闭 workspace-write;可通过配置开关启用 | 首次启动时选择策略(例如全部拒绝、平衡模式) | 默认开启;可以禁用 |
| 域名允许列表 | 是,支持通配符 + 端口 | 未披露(文档记载有开/关切换) | 是,支持通配符 + 端口 | 是, allowOut 支持通配符(基于 SNI/Host) |
| 许可证 | Apache-2.0 | Apache-2.0 | 未披露 | Apache-2.0(SDK) |
| 状态 | Beta 研究预览版;Windows alpha 版 | 随 Codex CLI 发布 | 已发布产品 | 已发布产品 |
核心要点
- srt 在操作系统层面沙箱化任意进程,无需容器。
- macOS 上使用 Seatbelt,Linux 上使用 bubblewrap,Windows 处于 alpha 阶段。
- 网络默认拒绝;每个连接都经过主机侧的允许列表代理。
- Anthropic 报告 Claude Code 权限提示减少了 84%(内部数据)。
- 允许列表的宽度才是真正的安全调节旋钮;宽泛的域名可能泄露数据。
快来看看 GitHub 仓库、 Anthropic 工程博客文章 以及 Claude Code 沙箱文档。所有功劳归于该项目的研究者。另外,欢迎在 Twitter 上关注我们,别忘了加入我们的 150k+ML SubReddit 并订阅 我们的通讯。等等!你在用 Telegram 吗? 现在你也可以在 Telegram 上加入我们了。
需要与我们合作推广您的 GitHub 仓库、Hugging Face 页面、产品发布、网络研讨会等吗? 与我们联系
本文《 认识 Anthropic 的 sandbox-runtime:面向 AI 智能体的操作系统级沙箱,无需容器 》首发于 MarkTechPost.