npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@heathhe/pi-guard

v0.1.0

Published

Session-scoped guard modes and OS-sandboxed Bash for Pi

Readme

@heathhe/pi-guard

Pi 的 session-scoped 执行护栏:safe、full-auto、yolo 三种运行模式,加上由 @anthropic-ai/sandbox-runtime 提供的 Bash OS sandbox。运行时不依赖 @gotgenes/pi-permission-system。

安装与启动

从 npm 安装:

pi install npm:@heathhe/pi-guard

本地开发:

npm install
pi -e ./extensions/pi-guard.ts --safe
# 或使用 wrapper(安装包后)
pi-guard --full-auto [...pi args]

CLI flag --safe、--full-auto、--yolo 互斥。extension 也注册同名 Pi flags,因此显式加载 extension 时可使用 pi --yolo。初始 mode 按 Pi flag → PI_GUARD_MODE → 全局 defaultMode 解析。

从其他权限扩展迁移时,请在同一 Pi 进程中禁用旧扩展。多个权限扩展会各自收到事件,旧扩展仍可能弹窗或阻断;pi-guard 不会修改或接管它们的配置。

配置

唯一持久配置是 ~/.pi/agent/pi-guard.json。它是创建新权限会话时使用的默认模板,不是进程内所有 live session 共用的动态 policy。/guard settings 验证并原子写入默认模板,同时只把候选配置应用到发起命令的当前 session;其他已运行 session 保持各自的 effective config、mode、grants、identity、policy generation 和 sandbox worker,之后启动的 session 使用新默认。运行时 mode 切换不会改写 defaultMode,也不会写 Pi session entry。

{
  "defaultMode": "full-auto",
  "networkMode": "proxy",
  "allowDomains": ["github.com", "*.github.com"],
  "denyDomains": ["tracking.example.com"],
  "allowLocalBinding": false,
  "allowCommands": ["git", "echo"],
  "denyCommands": ["curl"],
  "commandRules": [
    { "pattern": ["git", { "anyOf": ["status", "diff", "log"] }], "decision": "allow" },
    { "pattern": ["git", "push"], "decision": "confirm", "justification": "Review the destination before pushing" },
    { "pattern": ["npm", "publish"], "decision": "deny", "justification": "Publish through CI" }
  ],
  "allowTools": ["memory_search"],
  "confirmTools": ["mcp_publish"],
  "denyTools": ["dangerous_tool"],
  "unknownToolPolicy": "confirm",
  "sensitiveDirectories": ["~/.ssh", "~/.aws", "~/.gnupg"],
  "readOnlyDirectories": ["~/shared-policy"],
  "protectWorkspaceMetadata": true,
  "writableDirectories": ["~/scratch"]
}

字段语义:

  • defaultMode:safe、full-auto 或 yolo;默认 full-auto。
  • networkMode:off、direct 或 proxy;默认 off。它只约束 pi-guard 包裹的 bash / user_bash 子进程,不是整个 Pi、MCP、浏览器或其他 extension 的全局网络开关。
    • off:sandboxed Bash 外连网络全禁;文件系统 sandbox 继续生效。
    • direct:允许进程直接建立原始 TCP/UDP 连接,不经过域名过滤代理;文件系统 sandbox 仍继续生效。此模式不会应用 allowDomains 或 denyDomains,应视为明确的网络扩权。
    • proxy:由 sandbox-runtime 网络代理按 allowlist-first 策略限制域名。
  • allowDomains:仅供 proxy 使用的 sandbox-runtime 域名 allowlist;默认 []。空 allowlist 在 proxy 下仍阻断所有外连;direct 下该字段被忽略。
  • denyDomains:仅供 proxy 使用的域名 denylist;默认 []。deny 先于并覆盖 allow,用于从较宽的 allow pattern 中 carve out 明确禁止的目标;direct 下该字段被忽略。
  • allowLocalBinding:在 off/proxy 下是否允许进程监听本机端口;默认 false。direct 已是不受网络限制的模式,因此该开关不形成额外限制。
  • allowCommands:仅在 safe 下减少普通 Bash 确认的命令 basename 表;默认 []。只有 Tree-sitter 能证明由静态 plain words 与 &&、||、;、| 组成的线性命令才会应用自动放行;变量展开、assignment、glob、重定向、substitution、control flow、subshell 和后台执行仍确认。
  • denyCommands:在 safe 和 full-auto 下都拒绝的命令表;默认 []。
  • commandRules:参数 token prefix 规则;默认 []。每条规则包含非空 pattern、allow / confirm / deny decision,以及可选 justification。pattern token 可以是精确字符串或 { "anyOf": [...] };多条规则或 compound command 同时命中时取最严格结果(deny > confirm > allow)。静态线性 shell 按每个 effective command 分别匹配;复杂 shell 只作为 ["bash", "-c", "<完整脚本>"] 匹配,因此不会被宽泛的 git allow 规则误放行。allowCommands / denyCommands 保留为 basename 兼容层。
  • allowTools / confirmTools / denyTools:工具名精确、大小写敏感的决策表;默认均为 []。三张表不允许出现同一个工具名。优先级为 deny → path/sensitive boundary → confirm → allow,因此 allowTools 不能绕过敏感目录、read-only carve-out 或写根边界。
  • unknownToolPolicy:没有出现在上述工具表、也不是内置 bash/read/grep/find/ls/write/edit 的工具如何处理;allow、confirm 或 deny,默认 confirm。确认按工具名与完整 canonical JSON input 的 SHA-256 精确复用;提示只显示经过 best-effort secret-key 脱敏和长度限制的 input。
  • sensitiveDirectories:完整的敏感读写保护目录表。guarded mode 下这些目录既加入 sandbox denyRead / denyWrite,也由 tool policy 硬拒绝;once/session 写根审批不能覆盖。省略字段时默认为 ~/.ssh、~/.aws、~/.gnupg;显式 [] 会保持为空,不会恢复默认值。settings UI 保存空表前会单独警告并再次确认。
  • readOnlyDirectories:额外的“允许读、默认禁止写”路径;默认 []。命中后进入 tool-scoped once/session/deny 写审批,不像 sensitiveDirectories 那样硬拒绝。
  • protectWorkspaceMetadata:默认 true,把每个启动 workspace 下的 .git、.agents、.codex 作为隐式 read-only 路径。写类 Git 子命令因此会先审批 .git/**;关闭前 settings UI 会单独警告。
  • writableDirectories:额外可写根目录;默认 []。启动 workspace 与 OS 临时目录始终是隐式可写根。

旧配置兼容规则:若文件省略 networkMode 但包含非空 allowDomains,加载时自动解释为 proxy,保持旧版域名 allowlist 行为;省略 networkMode 且 allowDomains 为空或不存在时解释为 off。下一次经 /guard settings 保存后会写入显式的新字段。

调用级网络授权

guarded Bash 的普通执行授权与网络能力是两个独立维度;允许一条 Bash 命令不等于允许它联网。pi-guard 对静态 literal 网络目标执行保守分类:HTTP/HTTPS 进入域名代理授权,localhost / *.localhost / loopback HTTP 进入独立 local-service 授权,MySQL/PostgreSQL/Redis/SSH/nc 等原始连接进入 raw-direct 授权。变量、substitution、Node/Python 脚本或配置文件决定的目标不会被静态解析结果当作安全边界,仍先在 baseline sandbox 中 fail closed。

获批网络调用始终在一次性的 ephemeral sandbox worker 中执行;baseline session worker 永远不通过 updateConfig() 临时扩权。临时 worker 携带当前完整 filesystem policy和本次已批准 write roots,执行结束、取消、超时或异常后尽力停止被管理的进程组。HTTP session grant 按 (requester session, tool, canonical host set, policy generation) 复用,因此同域名 URL 的 path/query 变化不重复提示;每次执行仍创建新 worker。deny domain、敏感路径、Git config/hooks和 policy generation 失效优先于 session grant。

raw-direct 的当前真实 enforcement 是“本次 exact command 的受管前台进程组临时获得不限目标的 outbound network(最长 300 秒)”,不是只允许 UI 中检测到的 host:port。UI 必须显示这个事实;raw session scope 只跳过相同 exact command 的后续提示,仍逐次使用 ephemeral worker。worker 结束时执行 best-effort process-group cleanup,但不能保证回收通过 setsid / double-fork 脱离该进程组的 daemon,所以 raw-direct 拒绝已识别的 compound、background 和 daemon-style 命令。若需要不可绕过的完整 descendant 回收,还需要 cgroup v2、macOS 专用 supervisor 或 Windows Job Object;当前实现不作这项保证。若需要对任意 MySQL/PostgreSQL 客户端强制做到单个 host:port,现有 sandbox-runtime 无法在不增加透明网络 broker/OS 重定向的情况下表达,pi-guard 不会把 direct 冒充成精确目标授权。

pnpm/npm/yarn/bun install/add/view、npx/pnpx、corepack prepare 等明确的动态包管理联网操作会在首次执行前直接请求 exact-command raw-direct 授权,因为目标可能来自 registry 配置与 lockfile,sandbox proxy 又可能只返回通用 403,--silent 甚至可能没有 stderr;显式 --offline 不触发网络授权。其他非 direct profile 中失败且 stderr 命中 EPERM、ENOTFOUND、Operation not permitted、resolve/network-unreachable 等特征时,pi-guard 会追加结构化 policy diagnostic:该现象与 sandbox 网络拒绝一致,不能据此断定 VPN、DNS、数据库或远端服务故障。动态包管理失败诊断仍作为低层防御,并明确失败也可能来自包元数据、认证或 registry 配置。失败命令不会自动重放,避免重复副作用;仅当用户或 Agent 显式重试同一 tool 的同一 exact command 时,才进入带完整 unrestricted 提示的 raw-direct 审批。失败指纹不适用于不同命令,policy generation 变化时清除。

配置使用严格 schema:未知顶层字段会拒绝加载/保存,避免安全键拼写错误被静默忽略。所有 list 必须是 non-empty string 数组,条目会 trim、去重并拒绝 NUL。目录支持 ~/,并在发布策略前通过 realpath/最近存在祖先方式 canonicalize;workspace、OS 临时目录和额外可写目录形成初始 canonical write roots,read-only 路径作为 denyWrite carve-out。这份 policy 同时用于 sandbox、Bash mutation/redirect 检查、built-in file tools 和带 path 字段的未知工具。

canonical writableDirectories 若包含 filesystem root、home 目录或 home 的祖先,会被诊断为 dangerously broad:root session 启动和 /guard status 显示 warning,settings UI 保存前要求额外确认。手工 JSON 配置不会被静默改写或拒绝;hard deny 与敏感目录保护仍生效,但这些 root 会显著扩大 sandbox 写边界和 full-auto 自动放行范围。

safe 和 full-auto 遇到初始 roots 外的可识别写入时不会修改全局 roots:root UI 逐项显示 requester、工具名、当前操作 summary、检测到的 canonical paths,以及真实授权边界的 canonical root patterns(例如 /outside/**)。选择恰好有三种:单次授权(仅当前调用)、整个会话授权或拒绝;取消按拒绝处理。多 root 请求会逐项展示全部 pattern,不把目录授权描述成 file-exact。

整个会话授权只保存在按 ctx.sessionManager.getSessionId() 路由的 SessionState 内存中,cache key 是 (toolName, canonical root)。后续请求必须是同一工具,并且本次全部 roots 都被该工具的 session grants 覆盖,才会免提示;例如 write 的 /outside/** grant 不会授权 edit、bash 或 user_bash。单次授权不写 cache,下一次相同请求仍会询问。两种批准都不会写入 pi-guard.json、不会加入 baseline allowedWriteRoots,也不会重启其他 session 的 sandbox worker。敏感路径、hard deny 与 denyCommands 在审批前决定,任何授权 scope 都不能覆盖。

safe 的普通确认同样提供且只提供 单次授权、整个会话授权和拒绝。普通 session grants 使用与 write-root grants 分离的内存 cache:Bash 按工具名与完整原命令的 SHA-256 精确匹配,bash 和 user_bash 互不复用;workspace 外 read 按 canonical exact path;root 内 write / edit 按工具名与 canonical exact path,正文变化不会扩大或缩小该路径授权。提示显示 requester、工具、受限长度的操作摘要和授权 target;write/edit 只显示行数、字符数等统计,Bash 会对常见 secret-like 参数做尽力脱敏。root 本地取消、提示异常或无 UI 均 fail closed;child 或更深 descendant 即使自身有 UI 也不会本地提示,而是使用下文的签名转发直接请求 root,root 不可达或协议失败同样拒绝。

模式与热切换

  • safe:普通 bash / write / edit 以及 workspace 外 read 使用上述 exact-target once/session/deny 授权;root 本地无 UI 时阻断,child/descendant 则必须转发给可用的交互 root。若 Bash AST 中每一个 effective command 都在 allowCommands 中,则只跳过这次普通授权。hard deny、用户 deny、敏感读写和 canonical 写边界仍执行;“越出 baseline write roots 的非敏感写入”使用独立的 write-root once/session/deny 审批,Bash 仍在 OS sandbox 中运行。
  • full-auto:root 内普通操作免确认;denyCommands、hard deny、敏感读写和 canonical 写边界仍执行;可审批的越界写仍要求 root 用户确认,Bash 在 OS sandbox 中运行。allowCommands 在此模式无效。
  • yolo:关闭审批、pi-guard policy、hard deny 和 sandbox,使用本地 Bash 与 unrestricted tools。状态栏会持续显示醒目警告。

统一决策优先级

以下结果按表格从上到下匹配,先命中者生效:

  • allow:不显示 pi-guard 用户提示;guarded Bash 仍受 OS filesystem sandbox 和所选 networkMode 约束。
  • confirm:由用户选择 exact target 的 once / session / deny;session 仅按同工具与 exact grant key 复用,取消、无交互 UI 或提示失败均拒绝。
  • write-root-approve:由 root 对 canonical write root 选择 once / session / deny;只有同工具的 session scope 可在当前 session 复用,拒绝或协议失败均不扩权。
  • network-approve:独立于 Bash/write-root 决策;HTTP/local-service 与 raw-direct 使用不同 capability,Root/Child grant 不互相继承,baseline worker 永不扩权。
  • deny:直接拒绝且 fail closed;后续 allowlist、普通确认或 session 审批都不能覆盖。

| 条件或入口 | safe | full-auto | yolo | |---|---|---|---| | parser、policy、sandbox 或 lifecycle 无法证明安全 | deny | deny | policy/sandbox 不运行 | | built-in hard deny(如 sudo、recursive forced rm) | deny | deny | allow | | 命中 denyCommands | deny | deny | allow | | 命中 deny commandRules | deny | deny | allow | | 命中 sensitiveDirectories 的读写 | deny | deny | allow | | 可识别的非敏感写入越出当前 write roots | write-root-approve(once/session/deny) | write-root-approve(once/session/deny) | allow | | Bash 未触发敏感/越界写检查,且命中 confirm commandRules | confirm | confirm | allow | | Bash 未触发敏感/越界写检查,且每个 effective command 都在 allowCommands | allow | allow(该字段不参与判断) | allow | | 其他未触发敏感/越界写检查的 Bash | confirm | allow | allow | | 非敏感 read 越出 workspace | confirm | allow | allow | | 非敏感 read 位于 workspace 内 | allow | allow | allow | | grep / find / ls 读取 sensitive path | deny | deny | allow | | 写入 workspace metadata 或 readOnlyDirectories | write-root-approve(once/session/deny) | write-root-approve(once/session/deny) | allow | | 非敏感 write / edit 位于 write roots 内 | confirm | allow | allow | | 命中 denyTools | deny | deny | allow | | 命中 confirmTools | confirm | confirm | allow | | 命中 allowTools 且没有触发更高优先级边界 | allow | allow | allow | | 其他未知工具且没有触发 path boundary | 按 unknownToolPolicy | 按 unknownToolPolicy | allow |

这里的“不可覆盖”指 guarded mode 内的优先级:allowCommands、普通确认和 write-root once/session approval 永远不能推翻 hard deny、denyCommands、敏感路径拒绝或 fail-closed 错误。显式切换到 yolo 会关闭整个 pi-guard 边界,而不是覆盖某一条拒绝规则。

根 session 可使用:

/guard status
/guard explain
/guard doctor
/guard settings
/guard safe
/guard full-auto
/guard yolo

/guard status 分区按工具列出当前 session 已批准的 ordinary exact targets 与 write root patterns;不会显示内部命令 hash,过长 target 会截断,条目过多时会显示省略数量。两类授权只存在于内存,session 结束后清空;mode/config generation 更新不会把它们并入 baseline,也不会把它们持久化,并会清空 exact/write-root grants、pending Bash grants 和并发 approval 状态,避免旧授权覆盖新规则。若配置包含 dangerously broad writableDirectories,status 同时列出 warning 并使用 warning 级别通知。status 还显示签名 policy generation、运行时 child launcher enforcement 和 custom agent 的声明式 extension coverage;/guard doctor 在 launcher 生效时把未显式声明 guard 的定义标为冗余诊断,而不是要求修改 agent 文件。

Ctrl+Shift+Y 快速进入/退出 YOLO;退出时回到内存中记住的最后一个 guarded mode(没有可用记录时回退到配置的 guarded default,再回退到 full-auto)。每次从 guarded mode 进入 YOLO 都必须经过交互确认;无 UI 或取消确认都是 no-op。

所有 mode 和配置生命周期操作使用当前 session 自己的串行队列;A 的切换、保存或 shutdown 不会排队、reset 或覆盖 B:

  • safe ↔ full-auto 只更新该 session 的运行态,不写进程级 mode environment,也不重启 sandbox。
  • guarded → YOLO 先发布 not ready,等待该 session 的 sandbox worker 停止后才发布 YOLO mode。
  • YOLO → guarded 先发布目标 guarded mode 与 not ready,再初始化该 session 的 parser/sandbox worker;全部成功后才变为 ready。
  • guarded 状态下 baseline filesystem/network policy 变化只替换当前 session 的 worker,并清空当前 session grants;交互式 once/session grants 自身不重启 worker。command/tool-only 变化不重启 sandbox。
  • 配置先验证并完成所需 runtime 准备,再使用同目录临时文件 + rename 原子持久化,最后发布到内存。reinitialize、network update 或持久化导致 runtime 一致性无法证明时保持 fail-closed,不降级到本地 Bash。

/guard settings 仅在交互 UI 中可用。它可选择 runtime/network/unknown-tool policy,切换本地监听与 workspace metadata 保护,并逐行编辑所有 list 字段。每行会 trim,空分隔行会丢弃;取消 select/editor/确认不会修改磁盘、runtime 或 lifecycle。/guard status 会显示当前网络与工具 policy,以及 read-only 保护数量。

Command policy 顺序与匹配

Bash 必须先由 Tree-sitter Bash grammar 成功解析。每条 guarded Bash 的顺序固定为:

  1. parser 与 built-in AST hard deny;
  2. denyCommands,以及取最严格结果后的 deny commandRules;复杂 shell 仅匹配显式 bash -c wrapper rule;
  3. 明显 mutation/redirect path checks、workspace metadata/read-only carve-out,以及写类 Git 子命令的本地或外部 metadata 检查;若唯一问题是需要额外写权限,则进入 once/session/deny 写根审批;
  4. 应用剩余的 confirm / allow commandRules;
  5. 仅当 shell 结构可安全拆分为静态命令时,在 safe 下检查尚未由 prefix rule 决定的 effective commands 是否属于 allowCommands;
  6. safe 普通确认或 full-auto 普通放行。

Command entry 按 AST 中 effective command 的basename/参数 token 精确、大小写敏感匹配;/usr/bin/git 的 effective basename 是 git。解析会穿透支持的常见 wrapper(例如 command、exec、env、nohup、xargs)。静态多命令或 pipeline 中只要有一个 command 未获 allow,safe 仍需确认;任一命中 denylist 或 deny prefix rule 则拒绝。

allowCommands 只是 convenience filter,不是完整 Bash 安全边界。它永远不能绕过 sudo/doas、shutdown/reboot、mount/umount、recursive forced rm 等 built-in hard deny,不能绕过路径和敏感目录检查,也不能关闭 sandbox。AST hard deny 与 command extraction 都不是完备的行为分析;实际 Bash filesystem/network 行为边界来自 OS sandbox。

!command / !!command (user_bash) 复用相同 parser、policy、safe 授权和 sandbox operations,但 ordinary session grant 与 built-in bash 按工具名隔离。parser、policy、sandbox 初始化或执行失败均 fail closed。

Tool gate

tool_call 将 read/grep/find/ls 作为只读工具处理:guarded mode 都拒绝 sensitive reads,safe 对 workspace 外读取做 exact approval。write/edit 与 path-bearing custom tools先应用 sensitive、read-only 和 write-root 边界。边界通过后,custom tools 再按 denyTools、confirmTools、allowTools 与 unknownToolPolicy 决策。Pi 的 tool definition 没有可信的 side-effect annotation,所以未知工具默认 confirm;exact prompt 对常见 secret-like key 做 best-effort 脱敏。hard deny、denyCommands、denyTools、敏感读写和 lifecycle 错误不可被任何审批覆盖。

Root 与 child

session_start 以 ctx.sessionManager.getSessionId() 建立独立 SessionState,并为每次 live root activation 生成随机 guard session ID 与 Ed25519 identity。root 的 mode、guard ID、public key、snapshot path/digest 和 grants 都不写入共享 process.env。snapshot 包含 mode、完整 GuardConfig、root workspace、guard extension digest、单调 generation 和 policy digest;每次发布创建不可变的 generation-<n>-<digest>.json,避免 wrapper 与 child 分别读取可替换 current.json 的竞态。mode/config generation 更新会清空当前 session grants,并使旧 generation child 在下一次受保护操作时 fail closed。

PI_SUBAGENT_CHILD=1 时 extension 要求 snapshot path、root session ID、approval public key 和 guard digest 完整匹配,并验证 snapshot schema、content digest 与签名。child 使用 snapshot 的 mode/config,不再读取自己的 ~/.pi/agent/pi-guard.json,也忽略 child flags/default。child 不能热切换 mode,也不能用 settings 修改 policy;必须从 root 操作。snapshot 缺失、篡改、session/digest 不匹配、parser/sandbox 初始化失败或 readiness channel 失败时,required child 调用 shutdown() 并保持所有工具 fail closed。

所有 descendant 都使用统一的 v5 签名审批协议直接向 root 转发 write-root、safe 普通 exact-target 与独立 network 请求,不由中间 child 代批。每一层把有界 PI_GUARD_APPROVAL_DEPTH 递增,超过 32 层拒绝初始化。request 是严格 discriminated union;全部公共字段与类型字段都进入 request digest,root 的 Ed25519 signed response同时绑定 request hash 和批准 scope(once 或 session)。未知字段、跨 kind 字段、旧版本、缺字段、scope 篡改、伪造、响应重放、marker mismatch、root 不可达、超时或协议/IO 错误都 fail closed。协议 session 目录要求当前用户拥有的真实 0700 目录,请求、响应和 ready marker 使用原子 0600 文件;预创建或符号链接异常拒绝启动/转发。

child 在 exact 转发前执行完整本地 policy 与 canonical/sensitive validation,验签批准后再次验证,只有发起 child 会把 session scope 写入自身 (toolName, grantKey) 内存 cache;root 不记录 child exact grant,once 不缓存。相同 child/tool/grantKey 的并发请求合并成一次 root prompt,但各调用仍独立执行;不同 target 不批量授权。同一 child 达到第 10 个不同 forwarded exact prompt 时,root 只警告一次,绝不会自动切换 mode 或扩大 scope。

Bash 的完整原命令仅在 child 本地用于 SHA-256 grant key;转发与 root UI 只携带 policy 生成的脱敏、截断 summary/authorization targets,内部 hash 不显示在 prompt、status 或普通日志。regex 脱敏是 best-effort:需要精确人工审查的高风险 Bash 应保持命令简短,稳定的常用安全命令优先加入 allowCommands。forwarded write-root session grant 同样只记录在发起 child,并按工具/root 隔离。每次 child 受保护操作都会验证 Root route、guard identity 和 policy digest仍存活;Root policy generation 更新、route replacement 或 shutdown 会撤销旧 child 的本地 session grants 使用路径并 fail closed。本阶段不提供 spawn-time preauthorization。

Git write root 只在启动时检测一次且不执行 shell:默认 metadata protection 把 workspace .git 作为 read-only root;.git 指针文件还会解析 gitdir/commondir,识别 workspace 外的 common metadata root;workspace 位于祖先仓库子目录时审批 canonical 祖先仓库根。commit、merge、rebase、reset、checkout、clean 等写类 Git 子命令会在执行前审批相应 root;status、diff、log 等普通只读子命令不会因此提示。漏检的实际写入仍由 OS sandbox 的 read-only carve-out 或 write allowlist 拒绝。

.git/config 与 .git/hooks/** 是更窄的强制 deny carve-out:pi-guard 对 workspace、linked-worktree common metadata 和祖先 repository root 都生成显式 canonical deny 路径。即使用户批准整个 Git metadata root、把它放进 writableDirectories,或关闭 protectWorkspaceMetadata,这些路径仍不开放;因此需要改写 config 的 git remote 等操作可能仍会被 sandbox 拒绝。该限制是刻意保留的,目前没有 allowGitConfig 扩权开关。

进程只共享一个无状态 PI_SUBAGENT_PI_BINARY wrapper 和 owner-only launch registry directory。每个 Root 在 registry 中拥有独立 route;direct child 由每次 spawn 的 PI_SUBAGENT_PARENT_SESSION 精确选择 route,wrapper 再向 child 注入 sticky PI_GUARD_ROOT_ROUTE_ID,因此 nested child 即使 immediate parent session ID 改变也保持原 Root。pi-subagents 的标准 foreground/background/parallel/chain/fanout/retry/resume/nested spawn 使用该 override;unknown、stale 或已 shutdown 的 route 一律 fail closed。wrapper 保留原 argv,并在 task 前强制追加显式 --extension <pi-guard.ts>,所以 agent 的显式/空 extensions:、defaultExtensions: [] 或 denyExtensions=true 不能从标准 child launch 中移除 guard。已有同一路径 wrapper 会加入共享 registry,不再误判为递归;原有不同 downstream launcher 仍被链式调用。

wrapper 为每个 child 创建 owner-only readiness channel。child 只有在 snapshot 验签、paths/parser/sandbox、hooks 与 approval client 初始化完成后才写入绑定 nonce/session/policy/extension digest 的 attestation;缺少或不匹配时 wrapper 终止 child 并返回非零。root 的 /guard doctor 仍只读扫描 user/project custom agent definitions,但 launcher 生效时 0 covered / N explicitly uncovered 只表示这些定义缺少声明式冗余,不再要求逐个编辑 subagentOnlyExtensions。pi-guard 不会修改任何 agent definition。

该强制加载保证仅覆盖遵守 PI_SUBAGENT_PI_BINARY 的标准 pi-subagents 执行链。其他 extension 自行 spawn 真实 Pi、直接 Node API 或用户绕过受控 root 启动入口,不在这一行为保证内。

安全边界

  • OS sandbox 当前只覆盖 built-in bash override 与 user_bash。每个 guarded live session 启动独立的 pi-guard-sandbox-worker 进程;每个 worker 独占一份 static SandboxManager、network proxy、bridge、credential registry 和 lifecycle。Desktop 主进程不再调用共享 SandboxManager.initialize/updateConfig/reset,因此 A 的 network 更新、YOLO 或 shutdown 不会改变 B。
  • 额外 Bash 写批准通过该 session worker 的 per-execution filesystem customConfig 生效:加入该调用的 extra roots,始终保留 sensitive deny,并且只在批准 root 与某个 read-only carve-out 完全相同时临时移除该 carve-out;批准其祖先不会顺带取消嵌套保护。macOS/Linux 已覆盖并发 worker、交叉 workspace 写拒绝和独立 teardown 集成测试。Windows 并发 helper 尚未完成 ACL/WFP 交叉验证,因此检测到第二个并发 guarded session 时 fail closed。Linux 仍需 sandbox-runtime 的平台依赖(如 bubblewrap)。
  • write / edit 依赖最终 tool_call gate,不是完整 filesystem broker。
  • 其他 extension、MCP、直接 Node API 或未经过该 gate 的执行路径可以绕过这些控制。
  • child wrapper、签名 snapshot 和 readiness attestation 提供标准 pi-subagents 路径的自动继承与 fail-closed 启动,不是对同进程恶意 extension 的不可绕过信任根。
  • 未知工具只对谨慎识别出的 path 字段做边界检查。
  • YOLO 明确绕过全部 pi-guard 控制;确认只降低误触风险,不改变其能力。
  • 这不是最终的 Codex 安全等价实现;sandbox-runtime 本身仍是 research preview,Linux 还需其平台依赖(如 bubblewrap)。

开发验证

git diff --check
npm run check
npm test
npm run pack:check