unexpected_user
严重
白名单 ALLOWED_USERS 之外的账号跑了 agent。告警携带 loginuid,于是“alice 通过 sudo 以 root 身份跑了 agent”是一个事实,而不是一个谜。
观测记录
agentwatchdog 从 agent 外部观察——在 /proc 里,“配不配合”不是一个变量。它绝不读取你的 prompt,而且有一条测试专门在它读了的时候失败。
claude -p "周五之前把预发数据库的凭据轮换掉" --model opus
secret_flags-p 后面跟的是 prompt
codex exec "给复盘会整理一份事故时间线" --sandbox read-only
位置参数 · 默认拒绝codex 的 prompt 就是裸参数
aider -m "修一下鉴权那个 bug" --api-key sk-EXAMPLE-NOT-A-REAL-KEY-000
secret_flags · 凭证形态两条规则各盖一次
spindle chat "起草迁移方案" --task "internal"
无指纹一个从没见过的 agent——所有位置参数照样全部拒绝
命令行是合成的,规则是真的。工具本身从不写下原始字符串——脱敏发生在落盘之前,所以你机器上并不存在一个可供还原的“之前”。
什么都不写,什么都不发。可以直接在你不放心的那台机器上跑。
缺口
agent 内部的可观测方案——OTel exporter、hook、wrapper 命令、代理——只在 agent 愿意配合时工作。不设那个环境变量、不装那个 hook、直接调用二进制,它就瞎了。而跑飞的 cron、配错的服务单元、不该出现在这里的账号,恰恰是永远不会配合的那些。
/proc两者都值得有。想要按 token 计费的仪表盘,请用 OTel 系的工具——本项目不是那个。这里做的是这台机器的审计日志。
检测器
每条告警都带着下一步该敲的命令。重复告警按“情形”而不是按“扫描次数”抑制——一个账号跑了五十个 agent 是一条结论,因为每六十秒响一次的监控,最终会被人关掉。
unexpected_user
严重
白名单 ALLOWED_USERS 之外的账号跑了 agent。告警携带 loginuid,于是“alice 通过 sudo 以 root 身份跑了 agent”是一个事实,而不是一个谜。
parent_spawn_storm
严重
同一个父进程在持续拉起 agent。人类不会这么干——这是重启循环,而且每次尝试都在计费。告警携带完整祖先链,因为 agent 是症状,父进程才是病灶。
user_high_frequency
警告
某个账号启动 agent 的速度,超过了人手输入的可能。
long_running_process
警告
非交互 agent 超过了时限。交互式会话与 SDK 会话被豁免:一个有人正坐在前面的会话不是挂死进程,把它当挂死处理,这个检测在它本该服务的机器上就废了。
high_cpu · high_mem
警告
持续性的资源异常。用生命周期均值并设了最小采样年龄——否则每个 agent 启动时都会飙的那一下,会变成每个 agent 都触发一次的告警。
agents_during_high_load
警告
多个 agent 挤在一台已经吃力的机器上。没有任何单个进程有错,而这正是别的工具都不会报它的原因。
隐私契约
这个工具靠读命令行吃饭。这件事只有在约束被强制执行、而不是被口头描述时才可接受——所以下面每一条都接着一个测试,CI 把它们作为独立 job 运行,失败不可能被忽略。
test_
任何代码路径都不会打开 /proc/PID/environ。测试记录一次扫描期间的每一次 open(),该文件一旦出现即失败。住在环境变量里的 API key,不可能经由这个工具泄露。
test_
命令行在写入之前就按 agent 各自的规则脱敏——因为每家 CLI 放 prompt 的位置都不一样。
test_
一个参数只有在指纹明确认可它是已知子命令时才能留下。认不出的 flag 一律按布尔量处理,于是它后面那个 token 也会被盖住。比这个版本更晚发布的 agent 泄不出任何东西,只是记录得更粗略。
test_
各家 key 前缀、JWT、长不透明 token,即使出现在没有任何规则预料到的位置也会被打码——而那恰恰是有人把命令粘错时凭证会去的地方。
test_
摘要是按固定形状从零拼装的,不是从事件里过滤字段得来的,之后还要在运行时对照 key 白名单校验。证明不了匿名的导出会被拒绝,而不是被发布。
跑一下 agentwatchdog selftest,看这些承诺对你机器上真实运行的 agent 是否成立——不是对我们的机器。
agents.d
每个 agent 是一份 JSON 指纹,描述如何识别它、以及它把 prompt 放在哪。运维者可以在 /etc/agentwatchdog/agents.d 里添加或修正指纹,不必等新版本;而“一份指纹 + 一条脱敏测试”是这个项目最欢迎的 pull request。
| Agent | flag 表 | prompt 在哪 |
|---|---|---|
| Claude Code | 已对真机核验 | 裸位置参数,以及 -p |
| OpenAI Codex CLI | 已对真机核验 | 位置参数,顶层与 exec 之后都有 |
| aider | 已对 0.86.2 核验 | -m、--message、--msg |
| Gemini CLI | 按文档编写 | -p、--prompt |
| OpenCode | 按文档编写 | run 之后的位置参数 |
真机核验抓到的两个坑,可以作为“别凭记忆写这种表”的佐证。其一,Codex 在顶层就接受位置 prompt,不只在 exec 之后。其二,Codex 的 -p 是 --profile,而 Claude Code 的 -p 是 prompt——把一家的规则搬给另一家,工具会认真地把 profile 名打码,然后把真正的 prompt 写进磁盘。两个坑现在都是回归测试。
安装
一个文件,无需构建,除 python3 外零依赖。仅支持 Linux——因为这套设计本身就是 /proc。
1 · 看
$ curl -fsSLO https://github.com/Ymakercc/agentwatchdog/releases/latest/download/agentwatchdog.pyz
$ chmod +x agentwatchdog.pyz
$ ./agentwatchdog.pyz dry-run
# 不写文件、不发消息、不更新任何状态
在你自己的机器上,看到一次扫描的完整输出——而此时工具还什么都没做过。
2 · 验
$ ./agentwatchdog.pyz selftest
对全部内置指纹、以及你正站着的这台机器上真实运行的命令行,验证脱敏规则。
3 · 装
$ sudo mv agentwatchdog.pyz /usr/local/bin/agentwatchdog
$ sudo agentwatchdog install
# systemd timer、配置、日志轮转。在 NOTIFY 另行配置之前,
# 告警只留在本地。