这是全书争议最大的设计决定,也是最容易被简化成「pi 不安全」的一条。要评价它,先得把两件常被混为一谈的东西分开:
把 project trust 当成沙箱,是使用 pi 时最危险的误解。它自己的文档也是这么强调的。
README 的措辞是描述性的:pi 不含用于限制文件系统、进程、网络、凭证访问的内建权限系统,默认以启动它的用户和进程的权限运行。README.md:40
docs/security.md 给出的才是论证本体:
This is intentional. Pi is designed to operate on local source trees, invoke project
toolchains, and integrate with the user's existing development environment. A partial
in-process sandbox would be easy to misunderstand as a security boundary while still
depending on the host shell, filesystem, package managers, credentials, and extension
code. Real isolation needs to come from the operating system or a
virtualization/container boundary.
紧接着一段把 project trust 的定位钉死:它只是输入加载的守卫,防的是仓库在你批准之前悄悄改 pi 的设置或扩展;它不会让不可信的代码、不可信的 prompt、或不可信的模型输出变得安全。security.md:37
同一段还有一句在同类项目文档里罕见的坦白:来自仓库文件、注释、文档、构建输出的 prompt injection,是本地 agent 的预期风险,pi 无法可靠防止。这不是免责声明的位置——它出现在解释设计的正文里。
触发条件是一份明确的清单——从当前目录发现以下任一项,就认为该项目「有需要信任才能加载的资源」:security.md:9–16
| 要信任才加载 | 无论如何都加载 |
|---|---|
.pi/settings.json | AGENTS.md |
.pi/extensions、skills、prompts、themes | AGENTS.override.md |
.pi/SYSTEM.md、.pi/APPEND_SYSTEM.md | CLAUDE.md |
项目 .agents/skills | 用户级/全局扩展、CLI -e 扩展 |
右边这一列值得盯一眼。上下文文件(AGENTS.md / CLAUDE.md)无视 project trust 一律加载。security.md:27 也就是说:clone 一个陌生仓库后启动 pi,即使拒绝信任,那个仓库的 AGENTS.md 也已经进了系统提示。
这在 pi 自己的威胁模型里是自洽的(prompt injection 本就在边界之外),但它把「拒绝信任」这个动作的保护范围压得比多数人直觉中窄得多。
-p、--mode json、--mode rpc 都不弹信任提示。没有已保存决定时,默认值 "ask" 与 "never" 都会忽略这些资源,只有 "always" 才信任。security.md:29
默认拒绝——对自动化场景是正确的方向。
| 方案 | 隔离的是 | 凭证在哪 |
|---|---|---|
| Gondolin(micro-VM 扩展) | 内建工具与 ! 命令 | 留在宿主 |
| Docker | 整个 pi 进程 | 进容器 |
| OpenShell | 整个 pi 进程 + 策略控制 | 可留在网关外 |
文档给了一条容易被忽略的提醒:扩展在 pi 进程所在的地方运行。用 Gondolin 把内建工具路由进 VM 时,其它自定义扩展的工具仍在宿主上跑,除非它们也自己做委派。containerization.md:17
还有一条同样实在:bind-mount 宿主工作区为读写时,容器/VM 里的写入照样改宿主文件。security.md:53 容器隔离的是进程,不是你挂进去的那个目录。
文档最后一段划出了「什么不算漏洞」:预期的本地 agent 行为、没有内建沙箱这件事本身、来自不可信内容的 prompt injection、用户自己装的扩展或 skill 的行为——除非报告能证明存在真正的权限边界绕过,或证明 pi 授予了本地用户原本没有的访问权。security.md:59
把边界写清楚本身是负责任的做法——它让安全研究者知道什么值得报,也让用户知道自己在承担什么。
锚点之后本章逐字引用的 security.md 与 containerization.md 在 25cc5c7(2026-09-22)的文档重写中被重写,引文在新版里大多找不到原句;论点没变:没有内建权限系统,project trust 只管启动时加载什么,security.md:33。新出现的一处值得注意:工具带上了 MCP 风格的 annotations,文档说「权限扩展」可以据此决定哪些调用要确认,extensions.md:167——确认逻辑仍然交给扩展,不在核心里。
「部分沙箱会被误当成边界」不是托词,它有真实的历史支撑:进程内的权限提示一旦存在,用户就会形成「pi 会拦住危险操作」的心智模型,而绕过它的路径多到数不清——一条 bash 命令、一个 npm 生命周期脚本、一个扩展、一个语言服务器。要拦住这些,你要重新实现的是操作系统。
而 pi 的目标场景恰恰要求这些能力全开:跑项目的 toolchain、装依赖、执行测试。一个「安全的编码 agent」如果不能跑 npm test,那它没解决用户的问题。
论证成立的前提是二分法:要么有真边界,要么没有。但现实里存在中间物,它们的价值不在于「拦住攻击者」而在于「拦住事故」:
~/ 的 rm -rf;git reset --hard;.env 内容 cat 进上下文然后发给 provider。这些不是「被恶意绕过」的场景,是「模型犯错」的场景,而模型犯错是高频事件。针对高频事故的确认提示,与针对攻击者的安全边界,是两个不同的产品;pi 用「不做安全边界」的理由把两者一起否掉了。
公平地说,pi 并非完全没有事故防线——AGENTS.md 里那份 git 禁令清单(第 17 章)正是干这个的,只不过它是写给模型的纪律,不是运行时的拦截。区别在于:纪律靠模型遵守,拦截不靠。
Claude Code 走了另一条路:有权限系统,工具调用按模式询问用户。它同样不是安全边界(一条被批准的 bash 命令可以做任何事),但它把「模型即将做一件不可逆的事」这个时刻暴露给了人。
两者的取舍差异可以概括为:pi 认为不完整的边界会误导用户,因此不提供;Claude Code 认为不完整的边界仍能减少事故,因此提供并说明它不是沙箱。这是产品哲学分歧,不是谁对谁错——但用户需要知道自己选的是哪一种。
值得注意的是,pi 的立场在它自己的目标用户身上更容易成立:一个能配 Gondolin micro-VM 的开发者,确实不需要进程内的假边界。
containerization.md。AGENTS.md 无视 trust 决定。拒绝信任一个仓库,仍会加载它的上下文文件。security.md:27 对「我只是想看看这个陌生仓库」的用户,「拒绝」这个按钮给的保护比它看起来的小。检查过并确认的:security.md 的 Security Policy 链接指向旧仓名 pi-mono(漂移 d2,见 漂移记录)。目前靠 GitHub 301 重定向仍可达,但安全策略链接依赖第三方重定向不是好实践。security.md:57
场景你 clone 了一个陌生仓库,启动 pi,在信任提示里选了「不信任」。这个仓库的 AGENTS.md 会进系统提示吗?
B。AGENTS.override.md / AGENTS.md / CLAUDE.md 无视 project trust 一律加载(除非整体关闭上下文加载)。project trust 管的是 settings / 扩展 / skills / 主题这类会改变 pi 行为的资源。security.md:27
场景你用 Gondolin 扩展把内建工具全路由进 micro-VM,同时还装了三个自己写的扩展工具。那三个工具在哪跑?
C。「Extensions run wherever the pi process runs.」Gondolin 覆盖的是 read/write/edit/bash/grep/find/ls 与 ! 命令;其它扩展工具除非自己委派,否则留在宿主。containerization.md:17
ac4ac9e(2026-08-07 快照)。