# hufu **Repository Path**: woye/hufu ## Basic Information - **Project Name**: hufu - **Description**: 一分为二,合而验之。 - **Primary Language**: Rust - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-17 - **Last Updated**: 2026-10-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # hufu(虎符) **不可逆操作的存证内核:不拦你,只证明谁批准了什么——并且事后改不掉。** An evidence kernel for irreversible operations. It does not block them; it produces an evidence pack, recomputable by a stranger, that names who approved what — and cannot be rewritten afterwards. 名字取自虎符:一半在君、一半在将,**合得上才作数**。这里的"两半"是账本里的 `meta.head` 和你抄走的 那份 digest——`hufu head` 打出来贴到账本之外(git commit、值班邮件、工单),日后 `hufu verify --expect-head` 拿它对。能改文件的人可以把整条链重算到自洽,**但他改不了你上周已经发出去 的那个值**。这是本仓库唯一挡得住内部人的机制,也是它和所有"自带校验"的审计产品最本质的差别。 **SQL 只是它的第一个用法。** `dbx-sql-guard`(宿主 DBX)拿它管 SQL 变更审批;换个场景,"提交了什么、 谁批的、第三方能不能重算"这三件事一个字都不用改——改库、删文件、发邮件、调支付接口,需要的都是同一套 东西。先做 SQL 不是因为它特殊,是因为它恰好是我们读过同类工具源码、能把缺口指到具体文件的那一个。 定位的来龙去脉见 [docs/POSITIONING.md](docs/POSITIONING.md)——那是立项期以 SQL 为唯一场景写下的 记录,读它当证据(同类工具怎么核实的、边界在哪),别当内核的说明书。 ## 三条不可让的条件 每个新需求先过这三问,三者都不加的不做: 1. **不可抵赖** — 内容与审批分别入摘要:事后改审批人不会破坏内容链,但会被 `verify` 单独点名。 2. **独立可验证** — 校验只需要这份 SQLite 文件和本仓库源码,不需要我们的服务器、我们的数据库或我们的凭据。 3. **不持凭据** — 内核从不连接任何数据库,也不接受连接串。它不知道你的密码,所以泄露不了它。 ## 安装 ```bash cargo install hufu # 从 crates.io(0.3.0 起) hufu --version ``` 从源码构建:`cargo build --release`,产物在 `target/release/hufu`。 **不想装 Rust 也要能验**:`python3 tools/verify_ledger.py ledger.db` 用标准库重算同一条链, 退出码同样是 0/1。格式说明在 [docs/VERIFYING.md](docs/VERIFYING.md)——那是给审计方看的, 不是给我们自己看的。 ## 快速开始 ```bash export HUFU_DB=$PWD/ledger/evidence.db # 不给就用 ~/.local/share/hufu/ echo "DELETE FROM orders WHERE created_at < '2020-01-01'" | hufu record \ --operator zaojiaoci --database shop --rows 120000 hufu audit --limit 5 hufu decide sg_xxxxx approve --approver li --note "DBA on duty" hufu verify # 篡改过就 exit 1,能直接进 CI hufu head # 把 digest 贴到账本之外:git commit / 邮件 / 工单 hufu verify --expect-head <那个 digest> # 唯一挡得住"整链重算"的检查 hufu export --out evidence.csv hufu rules > rules.json # 审批边界:谁能批、从哪级开始要批、哪些库豁免 hufu rules-set --from rules.json ``` 接成 MCP server(任何 MCP 客户端通用,stdio): ```json { "mcpServers": { "hufu": { "command": "/abs/path/hufu", "args": ["mcp"] } } } ``` ## 命令与工具 | 入口 | 用途 | 谁能调 | |---|---|---| | `analyze` | 风险分级,不落库 | CLI | | `record` | 追加一条待审记录 | CLI + `hufu_record_review` | | `decide` | 人工批准/驳回 | **只有 CLI** | | `audit` | 查账(JSON) | CLI + `hufu_audit_recent` | | `verify` | 重算证据链;`--expect-head` / `--expect-count` 与外部锚点比对 | CLI + `hufu_verify` | | `head` | 打印当前头指针(行数 + 末行 digest),供复制到账本之外 | CLI | | `export` | CSV 证据包 | CLI | | `rules` | 打印当前生效的规则文档 | CLI | | `rules-set` | 整份覆盖规则文档(输出即输入格式,缺字段/拼错字段都拒绝) | **只有 CLI** | | `mcp` | MCP stdio server(换行分隔 JSON-RPC 2.0) | — | **审批和规则都不做成 MCP 工具。** 给模型一个 `approve`,「谁批准了」这条证据就变成模型自述;给模型 一个改 `approvers` / `approvalFromLevel` 的入口,它就能自己把审批边界调到不存在。同理没有删除与撤销 工具:链是 append-only 的。`hufu_decide` 会返回 JSON-RPC `-32602`,`tests/stdio.rs` 把这条钉住了。 ## 已知限制 - **不拦截。** 没有执行前钩子,就没有真正的拦截。这里的产物是事后证据,不是事前防护。 - **提交人自报。** 存证证明「谁被记录为提交人」,证明不了「谁敲的键盘」。审批人同理: `decide` 只在 CLI 上,但任何有 shell 的人都能填任意名字——那是**流程边界**,不是安全边界。 - **词法引擎不是解析器。** 判定只能作为记录的字段,不能作为放行的唯一依据;改写语句可能绕过分类,绕不过证据。 - **时间锚定尚未实现。** 链能证明「这些行没被改过」,证明不了「这些行什么时候写的」——本机时钟可被调。 RFC3161 锚定是这条链变成合规证据的那一步。 - **整链重算挡不住,除非有外部锚点。** digest 不含密钥:能改这个文件的人可以改一行、把后面所有 digest 重算一遍、连 `meta.head` 一起改掉,`verify` 会认为一切正常。**唯一挡得住的是 `head` 的输出 已经被发到账本之外**(`verify --expect-head`)。所以 `hufu head` 的输出必须定期落到别处, 否则"不可抵赖"只是"没人改过"的同义词。 - **头指针只覆盖它建立之后的历史。** 删掉尾部行时,剩下的链完全自洽,只有 `meta.head` 记得 "本来应该有多少行"。旧账本首次打开时会补建基线并标记 `legacyBaselineMs`——从那一刻起可查, 之前那段历史没有这个保证。 - **文件权限只覆盖 unix。** 账本 `0600`、目录 `0700`(WAL 旁文件由目录挡住);Windows 上这一层是空缺。 - **整库被删时无证据。** `verify` 在零行时回 `checked=0`——链证明「已有的没被改」,不证明「文件还在」。 同样只有外部锚点能发现。 ## 状态 2026-10-12 从 `dbx-sql-guard` 0.4.1 的 sidecar 抽出:`model`/`util`/`risk`/`store` 原样搬入, `dbx_plugin_sdk` 零引用(那条依赖只在 adapter 的 `main.rs` 里)。 2026-10-21 补了三件让「证明力」配得上措辞的东西:**头指针**(`meta.head`,删尾部行现在能查出来)、 **外部锚定**(`head` + `verify --expect-head`,这是唯一挡得住整链重算的检查)、 **独立验证器**(`tools/verify_ledger.py`,不需要 Rust 就能重算,同时是交叉验证)。 145 个测试,其中 4 条专门跑 Python 那份实现,确认两套代码对同一条链给出同一个结论。 同一天还有两件事:**adapter 不再自带副本**(`dbx-sql-guard` 改成依赖本 crate)——两份判定会分叉, 而分叉那天「模型看到的结论」和「人看到的结论」就来自两套代码,审批留痕也就没有意义了; 以及**改名 `sqlnotary` → `hufu`**,格式一字未改,只有名字。 维护者发布流程见 [docs/RELEASING.md](docs/RELEASING.md)。 ## 协议 Apache-2.0,全文见 [LICENSE](LICENSE)。