开源编程 Agent pi 没有内置权限系统:三种隔离路线怎么选

2026-07-29

本文基于 pi v0.82.1(仓库 commit 027a584,2026-07-28)梳理,该项目仍在快速迭代,具体行为以官方文档 https://pi.dev/docs/latest 与仓库最新代码为准。

pi 把「没有权限系统」写进了 README 里 Permissions & Containerization 这一节,这不是还没做完,是它主动放弃了在进程内画安全边界这条路。 它的判断是:一个半吊子的进程内沙箱,会被人误当成真的安全边界,而它实际上仍然要依赖宿主的 shell、文件系统、包管理器、凭据和扩展代码。所以要么你接受它以启动它的那个用户账号的全部权限跑,要么你自己在外面套一层操作系统级的壳。中间没有档位。

这个取向直接决定了你怎么用它:你不能再指望在配置文件里勾几个开关就交差,安全动作全部前移到「你用什么方式把它启动起来」。

站内已有三篇讲通用方法论——工作区怎么切最小权限集怎么设计权限管得过头会付出什么代价;这篇不重复那些结论,只看 pi 这一个具体项目,把这套东西落到了哪几个文件、哪几条命令、哪几个配置项上,以及它诚实承认了自己管不了哪些事。

一、这句话的准确含义,比你想的更宽

README 的 Permissions & Containerization 一节原话是:

Pi does not include a built-in permission system for restricting filesystem,
process, network, or credential access. By default, it runs with the
permissions of the user and process that launched it.

注意它列的四类:文件系统、进程、网络、凭据。不是「没有文件写入确认弹窗」这种局部缺口,而是这四类全都不管。packages/coding-agent/docs/security.md 里说得更细:内置工具可以读文件、写文件、改文件、执行 shell 命令,权限就是 pi 进程的权限;扩展是 TypeScript 模块,跑在同一套权限里;包安装、shell 命令、语言服务器、测试命令,都是普通的本地进程,没有任何额外约束。

它给的理由值得抄下来当判断标准:真正的隔离必须来自操作系统或者虚拟化/容器边界。一个部分实现的进程内沙箱,只会制造安全感,因为它自己就跑在被保护对象的内部。

配套的态度体现在仓库根目录的 SECURITY.md 里。它的 Out Of Scope 列表把这几类明确排除在漏洞范畴之外:本地代码执行与沙箱行为、用户自己装的扩展与技能的行为、在不可信仓库里工作带来的风险、提示注入、以及任何前提是「攻击者已经能改你本地文件」的报告。除非你能证明是 pi 自己授予了那个访问权,或者跨越了操作系统的权限边界。

对你的实际意义是:出了事没人替你兜底,这句话是白纸黑字写着的。你在团队里推它,安全评审那关得靠部署方式过,靠不了产品特性。

二、项目信任拦的是「加载」,不是「执行」

pi 确实有一套叫项目信任(project trust)的机制,很容易被误读成权限系统。security.md 开头就把话说死了:它不是沙箱,也不限制你在某个目录里开工之后模型能让工具干什么。

它的触发条件是明确列举的。pi 从当前工作目录往上找,出现下面任何一项就认为这个项目有需要信任才能加载的资源:

  • .pi/settings.json
  • .pi/extensions.pi/skills.pi/prompts.pi/themes
  • .pi/SYSTEM.md.pi/APPEND_SYSTEM.md
  • 当前目录或祖先目录里的项目级 .agents/skills

一个空的 .pi 目录不算。交互式会话启动时,如果当前目录和它的父目录都没有存过决定,就走全局设置里的 defaultProjectTrust,默认值是 "ask",在有 UI 的时候弹窗问你。存下来的决定按规范化后的目录路径写进 ~/.pi/agent/trust.json,路径上最近的那条先生效,然后才轮到全局默认值。

关键在于它拦住了什么、没拦住什么。拒绝信任会跳过上面那些受保护资源,但 AGENTS.mdCLAUDE.md 这两类上下文文件照样加载,除非你整个关掉上下文加载。也就是说,一个恶意仓库改不了你的 pi 设置和扩展,但它往 AGENTS.md 里写一段指令、或者往某个源文件的注释里塞一段话,这条路是通的。SECURITY.md 直接承认了这一点:像 AGENTS.md 或者注释里的指令可以轻易地对编程 Agent 做提示注入,这防不住。想系统性理解这类攻击面,看提示注入的防御思路

还有几个容易踩的细节:非交互模式(-p--mode json--mode rpc)不会弹信任提示。没有可用的已存决定时,"ask""never" 都是忽略那些资源,"always" 则直接信任。单次运行想覆盖,用 --approve/-a--no-approve/-na。交互模式下可以用 /trust 把决定存下来,但它只写 ~/.pi/agent/trust.json,当前会话不会重新加载,得重启 pi 才生效。

另外,用户级/全局扩展和用 -e 从命令行加载的扩展,可以处理 project_trust 事件,第一个返回 yes 或 no 的扩展就拥有这个决定权,会把内置的信任提示压掉。这意味着你的信任策略是可编程的,也意味着一个你随手装的全局扩展有能力替你做这个决定。

组成部分它负责什么对应仓库位置你什么时候会碰到它
权限立场声明说明没有内置权限系统、默认继承启动用户权限README.md 的 Permissions & Containerization 一节第一次给团队做技术选型说明时
安全模型正文项目信任的触发条件、无内置沙箱的理由、跑不可信代码的建议packages/coding-agent/docs/security.md写内部使用规范、过安全评审时
漏洞范围界定哪些报告算漏洞、哪些明确 Out Of ScopeSECURITY.md合规问你「厂商责任边界在哪」时
三种隔离路线Gondolin / Plain Docker / OpenShell 的适用场景与配置packages/coding-agent/docs/containerization.md真正动手搭隔离环境时
工具路由示例扩展把内置工具的执行改道进微虚拟机packages/coding-agent/examples/extensions/gondolin/index.ts想改造成路由到自己的沙箱时
信任相关配置项defaultProjectTrust 的取值与写入位置packages/coding-agent/docs/settings.md批量下发团队默认配置时

三、三条路线各挡住什么、挡不住什么

containerization.md 把问题拆成两类:要么让整个 pi 进程跑在隔离环境里,要么让 pi 跑在宿主上、把工具执行路由进隔离环境。三条路线分别落在这两类里。

Gondolin:进程在宿主,工具进微虚拟机

Gondolin 是一个本地 Linux 微虚拟机。官方给的示例扩展在 packages/coding-agent/examples/extensions/gondolin/,用法是:

cp -R packages/coding-agent/examples/extensions/gondolin ~/.pi/agent/extensions/gondolin
cd ~/.pi/agent/extensions/gondolin
npm install --ignore-scripts

然后在你要挂载的项目目录里启动:

cd /path/to/project
pi -e ~/.pi/agent/extensions/gondolin

它把宿主的当前工作目录挂到虚拟机里的 /workspace,并接管 readwriteeditbashgrepfindls 这七个内置工具,用户输入的 ! 命令也一并路由进去。/workspace 下的文件改动会写穿回宿主。

index.ts 能看到几个实现细节值得你知道:它在 session_start 时创建虚拟机、session_shutdown 时关掉;只把 localCwd 一个目录挂进 /workspace;还注册了一个 gondolin 命令用来看虚拟机状态;并且在 before_agent_start 里改写系统提示中的工作目录那一行,让模型知道自己在虚拟机里、宿主工作区挂在哪。这个细节挺重要——如果不告诉模型,它会按宿主路径去想事情,然后一路报错。

它挡住的:模型跑出去乱写你 home 目录、乱装全局包、把测试命令跑成删库。挡不住的:/workspace 是写穿的,虚拟机里对项目文件的破坏照样落到宿主。而且这条路线的核心取舍是认证信息留在宿主——这是它相对整进程容器的最大优势,也意味着模型调用链路本身没被隔离。

代价也实在:@earendil-works/gondolin 对 Node.js 有最低版本要求(containerization.md 的 Requirements 一行写明了具体下限,以那里为准),还得自己装 QEMU。

Plain Docker:最简单的那条

整个 pi 进程进容器。官方给的 Dockerfile.pi 基于官方 Node.js 的 Debian bookworm-slim 镜像(具体标签见 containerization.md),装上 bash、ca-certificates、git、ripgrep,然后带 --ignore-scripts 全局装 @earendil-works/pi-coding-agentWORKDIR 设成 /workspaceENTRYPOINT 就是 pi。运行是这样:

docker run --rm -it \
  -e ANTHROPIC_API_KEY \
  -v "$PWD:/workspace" \
  -v pi-agent-home:/root/.pi/agent \
  pi-sandbox

这里有两个必须看懂的点。第一,-e ANTHROPIC_API_KEY 意味着厂商密钥进了容器,文档在路线对比表里专门标注了这条。容器里跑的任何东西——包括模型让它跑的任何命令——都能读到这个环境变量。第二,-v pi-agent-home:/root/.pi/agent 用的是命名卷,目的是让设置和会话留在容器本地;文档明确警告:如果你图省事把宿主的 ~/.pi/agent 挂进去,等于把宿主的认证文件和会话文件一起交出去。这一条和密钥管理的基本纪律是同一件事,只是这里的泄露路径更隐蔽。

它挡住的:对宿主文件系统的越界访问(/workspace 之外)、对宿主进程和工具链的污染。挡不住的:容器内的凭据外泄、以及同样写穿的 /workspace

OpenShell:策略化的那条

第三条是用 NVIDIA OpenShell,一个带策略控制的沙箱,控制维度包括文件系统、进程、网络、凭据和推理。它可以通过本地网关(底层是 Docker、Podman 或某个虚拟机运行时)跑,也可以通过远程 Kubernetes 网关跑。每个沙箱都需要一个活跃的网关,得先注册再选中:

openshell gateway add <gateway-url> --name <name>
openshell gateway select <name>
openshell sandbox create --name pi-sandbox --from pi -- pi

这条路线里,整个 pi 进程、内置工具、! 命令、扩展工具全都在 OpenShell 的边界内执行。

它比前两条多出来的能力有两处。一是网络与进程层面的策略控制,前两条要靠你自己在 Docker 或虚拟机层面折腾。二是凭据:OpenShell 的 provider 可以把原始的模型 API key 留在沙箱外面,配置了推理路由之后,沙箱里的代码调用 https://inference.local,由网关在上游注入真正的凭据。你需要把 pi 配成使用对应的 OpenAI 兼容或 Anthropic 兼容端点。这一下就把 Docker 那条路线最难受的密钥问题解掉了。

代价是要多维护一套网关。而且如果网关是远程的,项目文件不会从宿主 bind-mount 进去,沙箱里的写入不会反映到你机器上——你得在沙箱里 clone 仓库,或者用 openshell sandbox uploadopenshell sandbox download 手动搬。

顺带一提,这三条路线的凭据处理差异,是它们真正的分水岭,比「隔离得严不严」更值得你先想清楚。如果你的团队用的是海外厂商的模型服务,还得多考虑一层:官方对中国大陆存在区域限制、不支持直连;市面上确实有第三方中转,但这里不做任何背书,也不给具体渠道,选型时自己评估合规与数据流向。各家的规则不同且会调整,以官方最新说明为准。

四、边界与代价:它明确不管的那些事

这个设计放弃了什么,得说清楚,否则你会在错误的地方找答案。

放弃了细粒度控制。 没有「这个目录只读、那个命令要确认」这一层。你能调的只有隔离环境的边界在哪,边界之内是全通的。想要「每次写文件都问我一下」这种体验,pi 本身不提供。

放弃了跨路线的一致性。 三条路线的隔离粒度、凭据位置、文件同步方式各不相同,切换路线基本等于换一套心智模型。团队里如果有人用 Gondolin、有人用 Docker,出问题的时候排查路径完全不一样。

扩展是个明确的漏洞点。 containerization.md 里写得很直白:扩展跑在 pi 进程跑的地方。如果你用宿主 pi 加工具路由扩展(也就是 Gondolin 那条路线),你自己写的其他扩展工具仍然在宿主上执行,除非它们也自己把操作委派出去。这意味着 Gondolin 那条路线的隔离是「内置工具级」的,不是「进程级」的,你装的每一个自定义扩展都是这层保护上的一个洞。

写穿是默认行为,不是 bug。 Gondolin 和 Docker 两条路线里,/workspace 都是读写挂载。security.md 给的对策是:需要更强保护就用只读挂载,或者把文件拷进拷出。这需要你主动配,默认不给。

提示注入完全不在射程内。 前面说过,仓库文件、注释、文档、上下文文件、构建输出里的注入,被官方定义为本地 Agent 的预期风险,pi 不做可靠防护。隔离能做的只是限制注入成功后的破坏半径。

不适用的场景也很清楚。 如果你的合规要求是「工具调用逐条审计、按策略放行」,pi 本体给不了,你得在 OpenShell 那一层做,或者换思路。如果你要的是多租户共享一个 Agent 实例,这套模型也不成立——它的整个假设就是单用户本地进程。

五、按风险分档怎么选

把选择简化成三个问题,按顺序问:

这次要处理的代码,我信不信? 自己维护多年的仓库、自己写的分支,宿主直接跑就行,隔离带来的摩擦不值。来路不明的仓库、别人发来的复现工程、从网上抓的样例,security.md 的建议是明确的:用容器、虚拟机、微虚拟机、远程沙箱或者策略沙箱,只放进去这次任务真正需要的文件和凭据。

我会不会全程盯着? 交互式一边看一边改,风险面小很多。无人值守的自动化、批量任务、CI 里跑的那种,属于文档点名要求隔离的场景。这类场景还要额外考虑非交互模式不弹信任提示这件事,配置得提前定好。

凭据能不能不进去? 这条决定你选哪一条路线。凭据必须留在宿主 → Gondolin。凭据可以进容器且你接受这个风险 → Plain Docker。凭据必须既不在宿主暴露给容器、又要能用 → OpenShell 的推理路由。

六、上手避坑清单

别把宿主 ~/.pi/agent 挂进容器。 会踩是因为你想复用已有的登录状态,-v ~/.pi/agent:/root/.pi/agent 写起来太自然了。后果是宿主的认证文件和历史会话全部暴露给容器里的进程。改用命名卷,容器里重新做一次认证。

别在 Gondolin 路线下假设扩展也被隔离了。 会踩是因为「我已经上了微虚拟机」这个心理暗示太强。实际只有那七个内置工具和 ! 命令被路由,你自己装的扩展照样在宿主跑。上这条路线之前,先把当前加载的扩展清单过一遍,判断每一个跑在宿主上能不能接受。

别忘了非交互模式不弹信任提示。 会踩是因为你在本地交互式用得好好的,一搬到脚本里加个 -p 就变了行为:没有已存决定时,"ask""never" 都是静默忽略项目资源。结果是你的项目扩展没加载,Agent 行为跟本地对不上,你却以为是模型的问题。脚本里显式写 --approve--no-approve,别靠默认值。

别指望 /trust 立刻生效。 会踩是因为大部分 CLI 的配置命令都是即时的。pi 的 /trust 只写 ~/.pi/agent/trust.json,当前会话不重新加载,得重启。改完发现没变化时先想到这条,别去翻别的地方。

别忽略 Gondolin 的环境前置条件。 会踩是因为你按文档 npm install --ignore-scripts 装完就直接跑了。它对 Node.js 有最低版本门槛,还需要系统里装好 QEMU——后者得走你的包管理器单独装,不在 npm 依赖里。启动失败先查这两项,版本下限照 containerization.md 里的 Requirements 行核对,别凭印象。

别在远程 OpenShell 网关下等着文件自动同步。 会踩是因为前两条路线都是 bind-mount,习惯了。远程网关下沙箱里的写入不会回到你机器上,得用 upload/download 命令搬。跑完任务发现本地什么都没变,别怀疑 Agent 没干活。

装扩展和装包时保持 --ignore-scripts 的习惯。 会踩是因为默认的 npm 行为会执行生命周期脚本,而这些脚本跑在你的宿主权限下。pi 仓库自己的文档里,从示例扩展安装到 Dockerfile 里的全局安装,用的都是这个参数,这个惯例值得沿用到你自己的脚本里。

收束

pi 这套设计的价值不在于它有多安全,而在于它把边界画在哪儿说得足够清楚,让你没法糊弄自己。你可以不同意这个取向,但你没法说不知道。

上手之前留三个自检问题:这次跑的代码来路我清楚吗、模型密钥现在在哪个进程能读到、如果 Agent 把 /workspace 下的东西全删了我多久能恢复。第三个问题的答案如果不是「一条 git 命令」,那先解决它再谈隔离。

接下来该读哪个文件:先 packages/coding-agent/docs/security.md 通读一遍建立心智模型,再按你选定的路线去 containerization.md 对应的那一节抄配置;如果你打算改造成路由到自己的沙箱,packages/coding-agent/examples/extensions/gondolin/index.ts 那五百来行是现成的模板,工具接管、会话生命周期、系统提示改写三件事它都示范到了。项目采用 MIT 许可证,主仓库在 https://github.com/earendil-works/pi ,截至 2026 年 7 月在 GitHub 上约有 8 万 star。

本篇属于一个把开源编程 Agent 项目 pi 逐层拆开讲的系列,整体地图见开源编程 Agent pi 是什么;沿着这条线往下,还可以看 开源编程 Agent pi 上手实录开源编程 Agent pi 的上下文压缩

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。