Skip to main content

phocinae-guard · L0 内核

斑海豹(phocinae)命令行审批门 L0 内核:一条 shell 命令进来,判定出去 (allow / deny / ask + exit code)。面向 Claude Code / Cline / Codex CLI / Crush / dsh / Gemini CLI / Qwen Code / Kimi Code / OpenHands / Hermes 的 hook / 包装通用形态;本次交付为通用 CLI 形态(stdin 或 --command)。

定位

  • 审批门而非沙箱:只做「放行 / 拒绝 / 需人工确认」判定,不承诺隔离执行环境。
  • 单文件 guard.py,Python ≥ 3.8,零第三方依赖(纯标准库),可直接塞进任何 hook 管道。
  • L0 确定性表独立存活:服务器挂了也能守住黑白名单底线。

架构(三层)

命令 → 归一化
  ├─ L0 确定性表(纯离线):白名单精确/前缀 → allow
  │                         黑名单危险正则 → deny(先于白名单判定)
  ├─ L1 /v1/systemone 两问:noul「是否放行」+ score「风险层 2–10」(默认关闭,见下)
  │      阈值映射(通道合成取最严格):
  │        risk≥7 或 noul=false(≤0.35) → deny
  │        risk 4–6 或 noul 摇摆        → ask(hook 阻塞态,可 --confirm)
  │        其余(risk<4 且 noul=true)  → allow
  └─ 服务器不可用 → fail-closed:除白名单外一律 deny/ask(默认 deny,可配 ask)

不变式:只有 allow 类能过门(exit 0);deny/ask 均非零退出;
--confirm 只能把 ask 升级为 allow,永远不能覆盖 deny。

判定矩阵(noul 阈值 0.65 / ask≥4 / deny≥7,均可配):

risk noul=false noul 摇摆 noul=true
≥7 deny deny deny
4–6 deny ask ask
<4 deny ask allow

L0 表内容概要(l0_table.json,24 条黑名单正则)

类 覆盖
危险删除 rm -rf /、/*、~、$HOME、/etc /usr /var /boot /bin /sbin /lib /opt /root、rm -rf ./..
sudo 破坏 sudo rm/dd/mkfs/fdisk/sfdisk/parted/wipefs/shutdown/reboot/poweroff/halt
下载即执行 curl|sh、wget|bash、任意管道喂 shell、sh -c 'curl…'、eval $(curl)、source <(curl)
git 破坏 push --force/-f、push --delete(--force-with-lease 有意放行到 L1 由模型裁决)
find/xargs 删除 find / -delete、find /usr… -delete、find -exec rm、xargs rm
块设备 dd of=/dev/sd*/hd*/vd*/nvme*/mmcblk*/dm-*/md*/loop*/disk、mkfs.*
权限/属主 chmod [-R] 777、chown … /系统路径
其他 fork bomb(:(){ :|:& };:、%0|%0)、shutdown/reboot/poweroff/halt、crontab -r、> /etc… 重定向覆写、mv 进系统路径

白名单:ls/pwd/git status|diff|log|show|fetch|branch|tag 等只读命令(精确+前缀两档), 服务器不可用时这些命令仍可离线放行。

目录结构

phocinae-guard/
├── guard.py               # 单文件审批门(纯标准库)
├── l0_table.json          # L0 确定性表(白名单+黑名单正则)
├── guard.conf.example.json# 配置文件示例
├── mock_server.py         # 本地测试 mock(POST /v1/systemone 固定 noul/score)
├── mock_rules.json        # mock 按命令区分的规则(灰带命令用)
├── test_guard.py          # 验收测试(replay battery + 全链路)
├── .claude-plugin/        # Claude Code 插件(本地成品;市场提交为后续迭代)
│   ├── plugin.json        # 插件 manifest(name/version/description/author/hooks 声明)
│   ├── hooks/
│   │   ├── hooks.json     # PreToolUse(Bash) command 型 hook 配置
│   │   └── guard_hook.py  # hook 本体:Claude Code JSON 协议 ↔ guard.py 裁决
│   ├── skills/
│   │   └── phocinae-guard/SKILL.md  # 「审批门」技能说明(应对守则+自查清单)
│   └── scripts/
│       └── self_test.sh   # 插件自测(hook 链路 + exit code 断言)
├── README.md
└── LICENSE                # Apache-2.0

安装

mkdir -p /opt/phocinae-guard
cp guard.py l0_table.json /opt/phocinae-guard/
# 可选:全局配置文件(路径、阈值、L0 表路径都在这里改)
cp guard.conf.example.json /etc/phocinae-guard.conf.json

依赖:仅 Python ≥ 3.8。L1 判定需要一个 /v1/systemone 服务(phocinae-server P0 扁平协议,或测试用 mock_server.py)。P0 口径:L1 默认关闭(l1_enabled=false)—— 当前 150M 权重未经命令审批域校准(2026-10-08 联合实测:对灰区命令的 noul/score 信号≈噪声),灰区命令走 fail-closed(deny/ask)。领域微调 + 标定电池完成后(P1) 再开 PHOCINAE_GUARD_L1_ENABLED=1。不装服务也可用——L1 关闭时灰区自动进入 fail-closed。

用法

# stdin 收命令
echo "rm -rf /" | guard.py            # exit 1,deny
# 或显式传
guard.py --command "git status"       # exit 0,allow
guard.py --command "npm install -g x" # exit 2,ask(需人工确认)
guard.py --command "npm install -g x" --confirm   # 人工确认后 exit 0

exit code:0=allow(可执行)· 1=deny(拒绝)· 2=ask(阻塞待人工,hook 输出阻塞态)· 3=用法/配置错误。stdout 默认一行 JSON(含 decision/layer/reason/noul/score/ thresholds/exit),--text 换人读格式。审计 JSONL(ts+command+cwd+decision+依据层+原因) 默认追加到 ./phocinae-guard.audit.jsonl,--audit off 或 env 置 off 可禁用。

集成示例

通用 bash 包装(Claude Code / Cline / Crush / dsh 等都可用):

#!/usr/bin/env bash
# guard-wrap.sh:先过门再执行
out=$(/opt/phocinae-guard/guard.py --command "$1")
case $? in
  0) shift; exec "$@" ;;                       # allow → 执行
  1) echo "[guard] DENY: $out" >&2; exit 1 ;;  # deny → 拦截
  2) echo "[guard] ASK: $out" >&2; exit 2 ;;   # ask → 阻塞待人工
  *) echo "[guard] ERROR" >&2; exit 3 ;;
esac

Claude Code PreToolUse hook(settings.json):

{"hooks": {"PreToolUse": [{"matcher": "Bash", "hooks": [
  {"type": "command",
   "command": "jq -r .tool_input.command | /opt/phocinae-guard/guard.py"}]}]}}

hook 收到的 stdin JSON 里取命令后喂给 guard:exit 0 放行;exit 2 输出 decision=ask 给前端做人工确认(确认后带 --confirm 重跑);exit 1 直接阻断。 其余工具同理:Codex CLI / Gemini CLI 用 sandbox_command_wrapper/command wrapper, OpenHands / Hermes 用 PreToolUse 类 hook——凡是能「拦命令、看 exit code」的位置都能挂。

Claude Code 插件(.claude-plugin/)

本地成品形态:一个 Claude Code 插件目录——PreToolUse(Bash) 拦截每一次 shell 命令,交给 guard.py 裁决,再把结果翻译成 Claude Code hook JSON 协议 (permissionDecision: allow/deny/ask + 可执行理由)。本次仅本地成品,不发布; claude-community 市场提交为后续迭代。

插件结构

  • .claude-plugin/plugin.json:manifest(name/version/description/author + hooks/skills 组件声明,Apache-2.0);
  • .claude-plugin/hooks/hooks.json:PreToolUse → matcher Bash → command 型 hook python3 "${CLAUDE_PLUGIN_ROOT}/.claude-plugin/hooks/guard_hook.py" (timeout 30s,远低于默认 60s 上限);
  • .claude-plugin/hooks/guard_hook.py:协议适配器(纯标准库)——stdin 收 PreToolUse 事件 JSON,取 tool_input.command 调 guard.py,按 exit code 映射: 0/allow → hook exit 0 + allow(跳过权限弹窗)、1/deny → hook exit 2 + deny、 2/ask → hook exit 2 + ask(auto 模式下 ask 也强制弹窗);guard.py 出错/超时 → 无输出、exit 0,落入正常权限流(fail-closed,绝不自动 allow)。 传参逐项对齐:只显式传 --command / --cwd,不覆盖服务地址参数,沿用 guard.py 内置默认 http://127.0.0.1:8155;阈值/fail-closed/L1 开关/审计路径 等经环境变量透传(见「配置」节)。审计默认落 ~/.phocinae-guard/audit.jsonl (避免散落到各项目目录),PHOCINAE_GUARD_AUDIT=off 禁用;
  • .claude-plugin/skills/phocinae-guard/SKILL.md:教 Claude 理解裁决、被 deny 后 改方案而非原样重试、危险命令自查清单。

安装

# 本地开发/验证:claude --plugin-dir 直接挂本仓(团队分发走插件市场,后续迭代)
claude --plugin-dir /home/hermes/dev/phocinae-guard

依赖:python3 ≥ 3.8 在 PATH(hook 与 guard.py 均零第三方包)。不装插件时,可把 上文「集成示例」里的 settings.json hook 片段直接贴进 .claude/settings.json, 效果等同(插件只是把这个片段打包成一键安装)。

自测

bash .claude-plugin/scripts/self_test.sh

纯本地、纯 CPU(不起真实模型服务):两份 JSON 可解析+结构断言 → 服务地址对齐 断言(guard 内置默认 127.0.0.1:8155、hook 不覆盖)→ guard 直连(良性 exit 0 / 危险 exit 1 / 灰区 fail-closed)→ hook 链路(良性 exit 0+allow、危险 exit 2+deny、 灰区 exit 2+deny)→ L1 端到端(本地 mock 起在 8155 验证默认 server 对齐与阈值 映射,8155 被占则 SKIP)→ 审计 JSONL 校验。退出码 0 = 全绿。

行为要点

  • 默认只有 L0 表在跑(l1_enabled=false):白名单放行、黑名单 deny、其余灰区 fail-closed=deny——装上即最严格。体验过严时设 PHOCINAE_GUARD_FAIL_CLOSED=ask 放宽为人工确认档;L1 模型经命令审批域校准后再开 PHOCINAE_GUARD_L1_ENABLED=1。
  • deny 在任何权限模式下都生效(含 bypass/yolo 模式),--confirm 也无法覆盖; 被 deny 后 Claude 应依据理由改方案而不是重试同款。
  • 门只管 Bash 工具调用;Write/Edit/MCP 工具不受此 hook 约束(后续迭代可扩 matcher)。

配置

优先级:命令行 > 环境变量 > 配置文件 > 内置默认。

env 默认 说明
PHOCINAE_GUARD_SERVER http://127.0.0.1:8155 L1 服务基地址(自动拼 /v1/systemone)
PHOCINAE_GUARD_TIMEOUT 2.0 L1 请求超时(秒)
PHOCINAE_GUARD_NOUL_THRESHOLD 0.65 noul 放行阈值
PHOCINAE_GUARD_DENY_AT 7.0 score ≥ 此值 → deny
PHOCINAE_GUARD_ASK_AT 4.0 score ≥ 此值且 < deny → ask
PHOCINAE_GUARD_TABLE 同目录 l0_table.json L0 表路径
PHOCINAE_GUARD_AUDIT ./phocinae-guard.audit.jsonl 审计路径(off 禁用)
PHOCINAE_GUARD_FAIL_CLOSED deny 服务器不可用兜底:deny/ask
PHOCINAE_GUARD_CONFIG 无 配置文件路径(JSON,见 guard.conf.example.json)

对应命令行参数:--server/--timeout/--noul-threshold/--deny-at/--ask-at/--table/--audit/--fail-closed/--config。 L0 表自定义:JSON 的 whitelist/whitelist_prefix/blacklist 三节各自覆盖内置默认节; blacklist 条目形如 [名称, 正则, 说明]。

测试与验收

python3 mock_server.py --port 8155 --rules mock_rules.json &   # 本地 mock
python3 test_guard.py -v                                        # 验收套件

replay battery:15 条良性(ls/git status/pytest/npm test/echo/…)、24 条危险 (rm -rf /、curl|sh、sudo rm、git push --force、find -delete、dd、mkfs、fork bomb、 chmod -R 777、shutdown、crontab -r、重定向覆写 /etc 等)、4 条灰带 (npm install -g→ask、pip install→allow、curl 仅下载→allow、push --force-with-lease→ask)。

验收实测(2026-10-08):危险误放行 0/24,良性误拒 0/15(门槛 ≤1), L1 阈值映射 8 档全过(含边界 noul=0.65、score=4.0),服务器不可用 → fail-closed 行为正确(白名单离线放行、黑名单离线 deny、灰区 deny/ask 可配),审计 JSONL 落盘齐全, 自定义 L0 表与阈值 env 覆盖生效。测试全程只用本地 mock,不触碰任何真实服务。

安全声明

  • L0 正则表是确定性启发式,不是完备防护:换行/字符串拼接/多阶段下载再执行/base64 混淆等手法可绕过正则;它负责「宁严勿松」的第一道确定性拦截,不是安全的全部。
  • L1 判定依赖服务与模型质量:noul/score 是模型输出,阈值映射只是把模型意见翻译成 门禁动作;服务不可用默认 fail-closed=deny(最严),可用 PHOCINAE_GUARD_FAIL_CLOSED=ask 放宽为人工确认档。
  • 不变式:只有 allow 类能过门;--confirm 只升级 ask,deny 永不可覆盖。
  • 审计是事后追责材料:JSONL 记录了时间戳+命令+cwd+判定+依据层,请定期复核; 审计写失败不阻断判定(宁可漏记不误杀),但会向 stderr 报错。
  • 生产建议:关键路径配容器/权限/网络隔离;guard 是审批门,不是沙箱。
  • 已知局限:多行脚本整体喂入时判定按整段文本归一匹配;--force-with-lease、 普通 find -delete(非根/系统路径)等有意留给 L1 裁决,L0 不覆盖所有变体。

License

Apache License 2.0 —— 见 LICENSE。版权 2026 phocinae-guard 作者(Nous Research)。

Metadata

Release files for phocinae-guard 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for phocinae-guard 0.1.0
File Size Uploaded
phocinae_guard-0.1.0.tar.gz 25.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for phocinae-guard 0.1.0
File Interpreter ABI Platform
phocinae_guard-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 46.5 kB

Release files / phocinae_guard-0.1.0.tar.gz

Download URL phocinae_guard-0.1.0.tar.gz
Size 25.0 kB
Tags Source
SHA-256 checksum
How to use checksums
a452e5f9e68e9772af00d4adb8922c7a6b5c1016426ebd5f469f35b366f8e01d
BLAKE2b-256 checksum
How to use checksums
4f14ee10c76ecfc8d2f8ff47c471f94d44bb236606e92ab3e6c4f95bafed1c26
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / phocinae_guard-0.1.0-py3-none-any.whl

Download URL phocinae_guard-0.1.0-py3-none-any.whl
Size 21.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f5a27e1fdc4f6bce3baa8cafb9c881c6519073d9fbdafb507a4aa99c0efd0494
BLAKE2b-256 checksum
How to use checksums
35660393d215d64fe38b6723cc71c41938052688d6539f101a5421b60fbe2e60
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page