@fanchaozz/pi-sentinel

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.

Packages

Package details

extension

Install @fanchaozz/pi-sentinel from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@fanchaozz/pi-sentinel
Package
@fanchaozz/pi-sentinel
Version
1.0.1
Published
Sep 22, 2026
Downloads
391/mo · 231/wk
Author
fanchaozz
License
MIT
Types
extension
Size
240.8 KB
Dependencies
0 dependencies · 1 peer
Pi manifest JSON
{
  "extensions": [
    "./"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

🛡 Pi Sentinel

pi coding agent 的敏感信息防护扩展 —— 入向 token 化 / 出向明文恢复 / 落盘零明文。

English


为什么需要

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)
user@example.com + 期望 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