@fanchaozz/pi-sentinel
v1.0.1
Published
A pi extension that protects sensitive information (PII & credentials) in coding-agent sessions: inbound tokenization, outbound plaintext restoration for tool calls, derived masked display, and zero-plaintext session persistence.
Maintainers
Readme
🛡 Pi Sentinel
pi coding agent 的敏感信息防护扩展 —— 入向 token 化 / 出向明文恢复 / 落盘零明文。
为什么需要
AI 编码代理在工作中不可避免接触敏感信息:日志里的手机号、配置文件里的密钥、数据库查询结果里的身份证号。这些值一旦以明文进入会话,就会:
- 发送给 LLM 供应商——脱离你的机器控制
- 持久化到会话历史(session.jsonl)——随分享/归档扩散
Pi Sentinel 在 pi 的钩子层拦截这三条通道,让敏感值对 LLM 不可见、对工具可用、对历史零残留。
核心能力
| 能力 | 说明 | 示例 |
|---|---|---|
| 🔒 入向 token 化 | 用户输入/工具输出中的敏感值替换为可逆占位符 | 13900000001 → 13900000001 |
| 🔓 出向还原 | 工具调用参数中的 token 自动还原为明文——LLM 能指挥工具,但看不到原值 | SELECT * WHERE phone='13900000001' → 工具收到真实号码 |
| 🎭 派生展示 | token + 脱敏事实,LLM 可见脱敏形态 | 13900000001\|138****0000 |
| 🧹 落盘兜底 | 会话历史写入前再过滤一遍,assistant 复述的明文也拦 | session.jsonl 零完整明文 |
| 📋 45 条内置规则 | 16 类敏感类型:PII(手机/身份证/银行卡…)+ 凭据(API key/JWT/私钥…)+ 29 个厂商 key 子项 | AWS/GitHub/Stripe/OpenAI/Anthropic… |
| ✏️ 自定义规则 | 正则/关键词两种匹配 + 规则级动作覆盖 | 匹配内部 VIP 会员号格式 |
| 🧪 派生算子 | 4 种类型无关算子 + “期望输出”自动反推参数 | 填原文+138****0000 → 自动得 mask(3:4) |
| 🖥 TUI 管理面板 | 策略/查询/状态三面板,全键盘操作 | /sentinel:policy |
| 🔐 加密存储 | 原值 AES-256-GCM 加密存本机 | ~/.pi/sentinel/store/ |
工作流程
不装插件时:明文直接穿透
你粘贴日志(含手机号 / API key 等敏感值)
│
▼
明文进入对话上下文 ──→ 随每次请求发送给 LLM 供应商
│
▼
明文写入 session.jsonl 会话历史(持久化在磁盘,随分享/归档扩散)安装插件后:三条通道全拦截
Pi Sentinel 接入 pi 的四类扩展钩子(下文流程中的 B 编号):
| 钩子 | 拦截点 | 作用 |
|---|---|---|
| B1 | input(用户输入) | 发送给 LLM 前 tokenize |
| B2 | tool_result(工具输出) | 返回对话前 tokenize + 落盘兜底 |
| B3 | message_end(助手消息完成) | 写入历史前兜底过滤 |
| B5 | tool_call(工具调用发出) | token 还原为明文,工具拿到真实值 |
你粘贴日志(含手机号 / API key)
│
▼ B1 入向钩子
┌──────────────────────────────────────────────┐
│ 检测管线(45 规则,五道阈值闸) │
│ 策略决策(类型默认 / 规则覆盖 / floor 保护) │
│ 替换执行 │
└──────────────────────────────────────────────┘
│
▼
LLM 看到 "订单 13900000001 key=<API_KEY:r_001>"
│
▼ LLM 生成工具调用(用 token 指挥)
│
▼ B5 出向钩子:token → 明文还原
工具收到含真实手机号的 SQL ✓ 任务可执行(明文只到工具进程,不进上下文/历史)
│
▼ B3 落盘钩子:历史写入前兜底过滤
session.jsonl: 零完整明文 ✓ P1 不变量流程中所有号码均为虚构演示值(139-0000-0001 风格),非真实号码。
示意流程刻意不展示任何真实号码——毕竟这是款隐私防护插件 ;)
安装
# pi 扩展安装(二选一)
pi install npm:@fanchaozz/pi-sentinel # npm 包(推荐)
pi install git:github.com/fanchaozz/pi-sentinel # GitHub
# 或手动 clone 到扩展目录
git clone https://github.com/fanchaozz/pi-sentinel ~/.pi/agent/extensions/pi-sentinel快速开始
# 1. 重启 pi,输入任意含敏感值的文本试试(下例为虚构演示号)
联系我 139-0000-0001 # → 联系我 <PHONE:p_001>
# 2. 查看状态
/sentinel # 状态面板:token 数 / 存储大小 / 类型分布
# 3. 管理规则
/sentinel:policy # 45 条规则列表
# ↑↓ 移动 · Space 启/禁 · Enter 循环动作 · e 派生配置 · n 新增自定义
# 4. 查询已 token 化的值(用户特权)
/sentinel:query # 列表 + Enter 看全文 + / 搜索配置
配置文件:~/.pi/sentinel/config.json(可用 SENTINEL_HOME 环境变量重定向)
{
"policyOverrides": { "phone": "derive" },
"customRules": [
{
"id": "vip-member",
"label": "VIP 会员号",
"type": "customer_id",
"match": { "kind": "regex", "pattern": "VIP\\d{8}" },
"strength": "mid",
"action": "derive",
"derive": { "op": "mask", "args": "3:4" }
}
],
"disabled": ["entropy:value"],
"ruleActions": {
"regex:cn-phone": { "action": "derive", "derive": { "op": "mask", "args": "3:4" } },
"regex:known-token:github-pat": { "action": "allow" }
}
}动作语义:
| 动作 | LLM 看到 | 原值存储 | 出向还原 | 典型用途 |
|---|---|---|---|---|
| tokenize(PII 默认) | 13900000001 | ✅ 加密 | ✅ | 脱敏且工具可用 |
| redact(凭据默认) | <API_KEY:r_001> | ❌ | ❌ | 高危凭据 |
| derive | 13900000001\|138****0000 | ✅ | ✅ | 脱敏展示 + 可逆 |
| allow | 原文 | ❌ | — | 显式豁免 |
派生算子(4 种,全部类型无关):
| 算子 | 参数 | 示例 |
|---|---|---|
| mask | 3:4(留头尾)/ !6:4(遮两端留中段)/ #*#*(等长模板) | 138****0000 |
| length | 无 | 11 |
| hash | short / full | a3f5e2… |
| regex_extract | 捕获组正则 | @(.+)$ → example.com |
TUI 反向推导:表单里填原文 + 期望输出,Enter 自动算出参数——
手机号 + 期望 138****8000 → mask(3:4)
身份证号 + 期望 ******出生日期段**** → mask(!6:4)
[email protected] + 期望 example.com → regex_extract(@(.+)$)命令参考
| 命令 | 说明 |
|---|---|
| /sentinel | 状态面板(任意键退出) |
| /sentinel:policy | 规则管理面板(45 内置 + 自定义) |
| /sentinel:policy set <type> <action> | 类型级覆盖(headless/脚本) |
| /sentinel:policy reset <type> / reset-all | 恢复默认 |
| /sentinel:query | 查询 store 中的值(默认脱敏,Enter 全文) |
| /sentinel:query <token> | 直接查指定 token 全文 |
| /sentinel:reset | 清空 store + 计数器(需确认) |
策略面板键位:↑↓ 移动 · PgUp/PgDn 翻页 · Space 启/禁 · Enter 循环动作 · e 派生配置(内置)/ 编辑(自定义)· n 新增 · x 删除 · r 重置覆盖 · / 过滤 · q/Esc 退出
查询面板键位:↑↓ 移动 · Enter 全文/脱敏切换 · / 搜索 · Esc 退出
存储与安全
~/.pi/sentinel/
├── store/<sessionId>.jsonl # 原值(AES-256-GCM 加密)
├── config.json # 你的策略配置(无明文)
└── audit/<sessionId>.jsonl # 审计日志(含证据哈希,无明文)- 原值只存在于加密 store,密钥派生自设备密钥
- 派生事实(
138****0000)可明文落盘——它不是原值 password/private_key受 floor 保护,不可低于 redact- 已知边界:用户授权派生后,LLM 理论上可从多个派生值拼凑逼近原值(派生不设防原则);插件保证完整原值本身不出现
隐私声明
- 所有检测/加密/存储 100% 本地,无任何网络行为
- 不收集遥测
- 审计日志仅含哈希指纹
开发
npm install
npm test # vitest 125 用例
npm run typecheck # tsc 零错误设计文档:DESIGN_v1.0.0.md(架构/数据流/验收标准完整版)
License
MIT
