Perch 是一款纯本地、只读的 macOS 刘海区监视器,为编程 agent 的工具调用提供实时风险评分和持久化后门检测,帮助用户在被自动批准的危险操作发生时第一时间收到警告。

# Perch
**你的 AI agent 整天都在执行 shell 命令。Perch 会监控每一个命令。**
这是一个专为 **Claude Code** 和 **Codex** 设计的只读安全监视器,驻留在
你的 Mac 刘海区中 —— 它会对 agent **执行**的每次工具调用进行风险评分,追踪
它**留下**的持久化后门,并在出现任何危险情况时立即警告你。绝对不会妨碍你的正常工作。
[](https://github.com/theMobiusStrip/perch/actions/workflows/ci.yml)
[](https://swift.org)
[](https://www.apple.com/macos/)
[](LICENSE)
[](#security-model)
## 为什么选择 Perch
**问题所在。** 编程 agent 代表你执行工具,你越是信任它们,就越少去仔细阅读每一个提示。在连续批准了二十次操作后,其中可能混入了一个 `curl … | sudo sh`,而你在无脑盲点中就同意了。如果你同时运行三个 agent,那个危险的操作往往被埋没在你*没在看*的那个终端里 —— 又或者它压根就没有提示,因为它匹配了某条允许规则,或者你正以宽松的权限模式运行。
**Perch 的作用。** Perch 会 hook 进 Claude Code 和 Codex,并从两个维度进行监控:**Actions** —— 在每次工具调用触发的瞬间进行离线风险评分,一旦发现危险操作,会将其作为 OS 通知显示,并在刘海区弹出一张红色警告卡片;**Footholds** —— 对持久化层面(config、hooks、memory、LaunchAgents)进行实时扫描,确保任何试图在会话结束后继续潜伏的劫持行为无处遁形。
**Perch 绝不会做的事。** Perch 在**架构设计上就是只读的**。它永远不会代表你批准、拒绝或阻止 agent —— 代码中根本不存在将决策写回的路径。批准操作始终保留在你的终端中。监视工具对其所监视的对象应当拥有零控制权。这同样适用于 git:worktree 审计在运行每个命令时都会加上 `git --no-optional-locks`,这样即使是 `status` 也不会写入 index;清理工作也只是提供一串 `git worktree remove` 命令并放到剪贴板里,由你自己来执行 —— Perch 绝不会执行任何删除操作。
## 功能
Perch 从两个维度监控你的 agent —— 它们现在正在**做什么**,以及它们**留下了什么**:
| | |
|---|---|
| ⚡ **Actions** — 对每次工具调用进行风险评估 | 在每次工具调用发生时进行离线启发式评分:`rm -rf`、`sudo`、`curl \| sh`、凭证读取、强制 push、原始 IP 流量,以及对 agent 自身核心大脑(`CLAUDE.md`、`~/.claude` 设置/hooks)的写入。遇到危险操作会触发 OS 通知;所有规则都集中在一个可读性极强的文件中:[`RiskAssessor.swift`](Sources/PerchCore/RiskAssessor.swift)。 |
| 🧭 **Footholds** — 实时监控持久化层面 | 独立的刘海区页面会扫描那些 agent 可能用来在会话结束后*继续潜伏*的文件 —— config/hooks、MCP 服务器、`CLAUDE.md`/memory、`LaunchAgents`、shell profiles —— 并显示它们的当前状态:最近被修改过、包含非 Perch 的 hook,或者无法读取。直接从磁盘读取,因此涵盖了 Perch 启动前发生的更改。 |
| 📡 **监控健康度** | 独立的覆盖状态条用于验证已部署的 bridge、本地事件 socket、Claude 连线以及 Codex hook 信任状态。引导式设置会安装或修复集成环境,确保“断连但保持静默”的应用与“运行健康”的应用有明显的视觉区分。 |
| 🔔 **即使没有任何提示也能报警** | 遇到危险操作会触发 OS 通知 —— 包括那些被允许规则或宽松权限模式自动批准的调用。最危险的调用,恰恰正是那些从来不会征求你意见的操作。 |
| 📊 **可解释的安全评分** | 刘海区和菜单栏会显示一个动态的 0–100 威胁态势评分:过去一小时内,每次危险操作扣 25 分,每次警告操作扣 5 分。展开状态条可查看计算公式和保留的近期检测记录;关闭警告卡片并不会抹除其历史记录。 |
| 🐦 **一目了然的所有会话** | 实时显示所有 Claude Code 和 Codex 会话的列表 —— 运行中 / 等待中 / 空闲,显示最后一条消息、上下文使用情况,并在任何刚刚执行了危险操作的会话上亮起红色 badge。 |
| 🎫 **Token 使用量** | 在刘海区显示今天 / 7天 / 30天的 token 使用总量,配备带有重置倒计时的 rate-limit 仪表盘,以及详细的按天 / 按模型 / 按项目展示的看板(菜单栏 → **Token Usage…**)。 |
| 🌳 **Worktree 清理维护** | 对 agent 会话留下的 git worktree 进行跨项目的只读审计 —— 分类为 `reclaimable`(干净、已合并、过期)、`review`(有未提交的更改或领先于默认分支)、`active`(正被活跃会话使用或最近被修改过)或 `orphaned` —— 显示磁盘占用大小,并提供*复制清理命令*的按钮(菜单栏 → **Worktrees…**)。Perch 只负责评分和报告;它绝不执行删除。 |
| 🪶 **零足迹** | 无依赖,无遥测,约 1.3 万行可审计的 Swift 代码。即使 Perch 崩溃退出,你的 agent 甚至根本察觉不到。 |
🧭 Footholds · 🌳 Worktrees · 📊 token dashboard screenshots
## Perch 能捕获哪些威胁
威胁模型:编程 agent 被 prompt injection 劫持(例如被植入恶意代码的仓库文件、网页或依赖项),或者是 agent 自身发生异常行为。Perch 将其监视的内容分为两种威胁类型 —— 瞬时威胁和持久威胁。
### ⚡ Actions — agent 正在做什么
每次工具调用在触发的瞬间都会进行离线风险评分。它们本质上是瞬时的:通过 hook 实时捕获,显示在卡片上,并在遇到危险时触发 OS 通知。
| 威胁 | 已捕获 | 示例 |
|---|:---:|---|
| **破坏性命令** | ✅ | `rm -rf`, `mkfs`, `dd`, 磁盘/设备写入, `shutdown` |
| **权限提升** | ✅ | `sudo …`, `chmod 777` |
| **远程代码执行** | ✅ | `curl … \| sh`, `wget … \| bash` |
| **凭证访问 (shell)** | ✅ | 读取 `~/.ssh`, `id_rsa`, `~/.aws/credentials`, `.env`, `security dump-keychain` |
| **写入 agent 的大脑** | ✅ | 写入 `CLAUDE.md` / memory (警告) 或 `~/.claude` 的设置/hooks (危险) —— 在发生瞬间即被捕获 |
| **历史记录 / 数据丢失** | ✅ | `git push --force`, `git reset --hard`, `kill -9` |
| **可疑的网络请求** | ✅ | 明文 `http://`,获取原始 IP 地址,`netcat` |
### 🧭 Footholds — agent 留下了什么
**Footholds** 刘海区页面直接从磁盘扫描持久化攻击面,并显示其*当前状态* —— 无需 hook,因此涵盖了在 Perch 启动前或 hook 关闭时所做的更改。这就是被投毒或被劫持的 agent 试图在会话结束后继续潜伏的地方。
| 攻击面 | 受监视 | Perch 显示的内容 |
|---|:---:|---|
| **Agent 配置** | ✅ | `~/.claude` 设置 —— 带有**非 Perch hook** 标记 —— 以及 `settings.local`,`~/.codex` 配置/hooks |
| **MCP 服务器** | ✅ | 从 `~/.claude.json` 自动启动的服务器数量 |
| **代码运行环境安装项** | ✅ | `~/.claude` 下的 `plugins` / `skills` / `commands` 目录 |
| **指令与记忆** | ✅ | `~/.claude/CLAUDE.md`、`memory/`,以及各个项目专属的 `CLAUDE.md` / `AGENTS.md` |
| **系统持久化** | ✅ | `~/Library/LaunchAgents`,shell profiles |
每一项都会标记为**最近已更改**、**非 Perch hook —— 需审查**,或者中性的**未更改**。Perch 绝不会断言一个文件是*安全的* —— 它只告诉你文件是否被更改过,或者是否包含它无法识别的 hook;一个未更改的文件仍然可能已被投毒。
所有的 Action 规则都集中在一个具备完善自测、可读性极强的文件中 ——
[`RiskAssessor.swift`](Sources/PerchCore/RiskAssessor.swift);而 Foothold
扫描逻辑位于 [`IntegrityScanner.swift`](Sources/Perch/Model/IntegrityScanner.swift)。
## 安装说明
### 选项 1 — 下载应用 *(推荐)*
**1.** 从
[**Releases**](https://github.com/theMobiusStrip/perch/releases) 页面下载最新的 `.dmg` 文件,
打开它,并将 **Perch** 拖入 **应用程序 (Applications)** 文件夹。
**2.** 首次启动 —— 批准应用运行一次。Perch 是开源的,仅在本地进行签名而非由 Apple 进行公证,因此 macOS 会要求你确认:
| macOS 15+ (Sequoia) | macOS 14 (Sonoma) |
|
1. 双击 **Perch.app** —— macOS 会提示 *"Perch" Not Opened*。点击 **Done**(千万别点 *Move to Trash*)。
2. 打开 **系统设置 → 隐私与安全性**。
3. 滚动找到 *"Perch" was blocked to protect your Mac*,然后点击 **Open Anyway**。
4. 进行身份验证(Touch ID / 密码),并在最后的对话框中再次确认点击 **Open Anyway**。大功告成 —— 以后它就能正常打开了。
|
1. 右键点击 **Perch.app** → **打开**。
2. 在弹出的对话框中点击 **打开**。
3. 搞定 —— 以后它就能正常打开了。
|
**3.** 完成全新安装后自动弹出的引导式设置,或者点击
菜单栏中的 Perch 小鸟图标 → **Monitoring Setup…**。在那里安装 Claude Code 和/或
Codex 监控(你现有的设置会被解析并合并、备份,
并且完全可以恢复 —— 参见[安全模型](#security-model))。
**4.** 重启任何正在运行的 agent 会话,并通过设置界面或菜单栏中的 **Run
Doctor** 验证一切是否正常。Doctor 和 hook 的安装过程会在后台运行,
因此菜单和设置窗口始终保持流畅响应。Codex 要求其 hooks 必须被显式信任后才会
运行它们;安装程序会自动记录该信任状态(这与 Codex CLI 的 `/hooks` 界面执行的写入操作完全
相同 —— 参见
[安全模型](#security-model))。如果自动信任失败(例如使用了旧版本的 Codex
CLI),安装报告会如实显示 —— 此时请在终端的
`codex` TUI 中运行一次 `/hooks` 命令(桌面版应用没有 `/hooks` 命令)。
**5.** 在 **Monitoring Setup…** 中,允许通知并选择哪些事件
类别应当打扰你。为了保持警告可见但又不会过于聒噪
(推荐做法 —— 你希望*看到*它们,而不是每次插旗提示都被叮一下),请关闭
**Play notification sounds**。macOS 的系统设置依然是管理
OS 横幅样式和权限的最终控制中心。
### 选项 2 — 从源码构建
任何 Swift 工具链都可以(仅需 CommandLineTools —— 无需安装 Xcode):
```
git clone https://github.com/theMobiusStrip/perch && cd perch
make run # build + assemble Perch.app + launch (no Gatekeeper dance)
```
然后像上面提到的那样从菜单栏注册 hooks,或者从终端执行:
```
dist/Perch.app/Contents/MacOS/Perch --install-claude-hooks
dist/Perch.app/Contents/MacOS/Perch --install-codex-hooks
```
### 验证你的下载 *(可选,但推荐)*
每个发布版本都附带了 `.sha256` 校验和以及 `.sha256.asc` GPG 签名。
将这三个文件下载到同一个文件夹中,然后执行:
```
cd ~/Downloads
# 步骤 1 — 完整性:DMG 与发布的 checksum 相匹配
shasum -a 256 --check Perch-*.sha256
# → Perch-x.y.z-arm64.dmg: OK
# 步骤 2 — 来源:checksum 由维护者的密钥签名
curl -fsSL https://github.com/theMobiusStrip.gpg | gpg --import
gpg --verify Perch-*.sha256.asc Perch-*.sha256
# → 签名有效
```
gpg 提示的 *"not certified with a trusted signature"* 警告是正常的 ——
签名是有效的;gpg 只是在提醒你尚未亲自将该密钥标记为
受信任。用于签名的密钥与为此仓库的 release tags 签名的是同一把 —— 你可以通过
` tag -v v0.3.0` 进行检查。完全不想信任预编译的二进制文件?
那就使用选项 2 —— 只需两条命令即可搞定。
## 工作原理
```
Claude Code / Codex ──hooks──▶ perch-bridge ──unix socket──▶ Perch.app
(your terminal) (fire & forget, ├─ risk scoring
keeps all decisions ~10 ms, exits) ├─ notch card + notification
└─ sessions / tokens / score
```
Hooks 会调用内置的 `perch-bridge`,该程序通过一个
权限为 `0600` 的本地 Unix socket 转发每个事件并随即退出 —— 每个事件都是纯观察性质的。`PreToolUse`
和 `PermissionRequest` 事件在到达的瞬间就会进行风险评估;
遇到危险操作会触发 OS 通知和一张刘海区卡片。与此同时,Perch 会实时追踪 (tail)
transcript/rollout 文件,并利用 `~/.claude/sessions` 中的
pid 文件验证会话的活跃度,因此就连在 Perch 启动前开启的会话也能被完全覆盖。
需要提醒的一点是:Claude 的 rate-limit 仪表盘数据是由 statusline payload 提供的,
而这部分 payload 只有在终端运行的 `claude` 会话才会渲染 —— Claude 桌面版应用从不
调用它。不过,检测功能、会话监控以及 Token 总量统计在任何地方都能正常工作。
对于刘海区卡片:按 **Esc** 键可关闭,使用 **←/→** 键可在队列中切换。该面板是一个
非激活状态的窗口 —— 当你的按键指令传达给 Perch 时,你的编辑器依然
保持着键盘焦点。
## 安全模型
Perch 守护着你的计算机,因此它对自己也秉持着同样的高标准 —— **其设计初衷是为了被审计,而不是被盲目信任**:
- **架构上绝对只读。** bridge 绝不会写回任何决策;
在源码的任何地方都不存在批准/拒绝的代码路径。Perch 无法
阻止 agent,也无法回应提示;hook 的开销属于
fire-and-forget 模式,约为 10 毫秒,如果 Perch 卡死,hook 会在
5 秒后自行放弃 —— agent 的运行永远不会被阻断。
- **100% 本地检测,零遥测。** 没有数据分析,没有云端检测
服务 —— Perch 观察到的任何内容都不会离开你的
计算机。代码库中唯一的网络
调用是可选的更新检查:一个对 GitHub releases API 的无身份验证 GET 请求,
默认开启,可在菜单栏中切换
(**Check Automatically**),关闭时实现完全零网络。你可以自行验证:
`grep -rn "URLSession\|NWConnection" Sources/` 的匹配结果仅限于
[`UpdateChecker.swift`](Sources/Perch/Model/UpdateChecker.swift)。
- **检测器无法泄露其检查的内容。** 风险评分纯粹是进程内的
字符串匹配过程;被标记的命令只会展示给你看,并写入你
本地的日志中,绝对不会发送到任何地方。
- **零依赖。** 仅使用了 AppKit/SwiftUI/Foundation。供应链
攻击面仅限于本仓库 —— 你可以从头到尾仔细审阅。
- **配置写入是精准且可逆的。** 安装
hooks 时会解析并合并你的 `~/.claude/settings.json` / `~/.codex/hooks.json`
(你的键值和 hooks 都会被保留),写入带有时间戳的备份,并
以原子方式替换。`--uninstall-*` 会恢复
所有内容,包括通过链式调用(而非替换)你现有的 statusline。
- **Codex hook 信任是显式、限定范围且公开透明的。** Codex 在
hooks 被信任之前会拒绝
运行命令 hooks。安装程序通过 Codex 自身的
`app-server` API —— 即与 `/hooks` 界面执行的完全相同的写入操作 —— 来记录该信任
并且仅针对命令为 Perch bridge 的 hooks,仅在你点击
安装时触发。该信任哈希绑定了确切注册的命令;如果随后有任何代码
编辑了这些 hook 条目,Codex 就会再次将它们降级为不受信任状态。
卸载操作会留下一些过期的哈希记录,但它们是完全惰性的:它们只能匹配
已被彻底移除的原始 Perch 条目,不会造成任何影响。
- **设计上的故障开放 (Fail-open)。** 如果 Perch 没有运行或崩溃退出,hooks 会
静默退出,你的 agent 的行为表现将与 Perch 根本不存在时完全一致。
## CLI
```
Perch --version print the app version
Perch --doctor integration + detection status
Perch --usage-report 30-day token usage, plain text
Perch --worktree-report cross-project stale-worktree audit, plain text
Perch --integrity-report persistence-surface scan, plain text
Perch --integrity-ack [id|all] mark flagged surface items as reviewed
Perch --selftest run the built-in test suite (600+ assertions)
Perch --install-claude-hooks / --uninstall-claude-hooks
Perch --install-codex-hooks / --uninstall-codex-hooks
Perch --trust-codex-hooks re-trust registered Codex hooks after a config change
```
## 开发说明
```
make debug # swift build
make test # build + run the selftest
make app # assemble ad-hoc-signed dist/Perch.app
make dmg # DMG + SHA-256 (+ GPG signature if a key is present)
```
CI 会在每次 push 时构建并运行自测;带有 tag 的推送 (`v*`) 会自动构建并
发布 DMG。文档中的屏幕截图都是使用
合成数据 (`Perch --render-showcase`) 在无头模式下渲染出来的 —— 绝不会提交任何真实的会话内容。应用图标是通过
[`scripts/gen-icon.swift`](scripts/gen-icon.swift) 生成的。
## 开源协议
[MIT](LICENSE)