RTK Hook 系统源码解析:从 rtk init 多 Agent 安装、SHA-256 完整性校验到 Deny > Ask > Allow 权限判决模型
2026/9/7 3:06:13 网站建设 项目流程

RTK Hook 系统源码解析:从 rtk init 多 Agent 安装、SHA-256 完整性校验到 Deny > Ask > Allow 权限判决模型

【免费下载链接】rtkCLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies项目地址: https://gitcode.com/GitHub_Trending/rtk4/rtk

RTK(Rust Token Killer)的 hook 层负责让 AI 编程助手自动将原始 CLI 命令重写为 RTK 等价命令,从而在不依赖用户手动配置的前提下获得 token 节省。本文以仓库内 src/hooks/README.md 为骨架,结合 init.rs、integrity.rs、permissions.rs、rewrite_cmd.rs 等源码实现,系统讲解rtk init的安装模式、PatchMode 补丁策略、五态完整性校验、原子写入机制与跨 Agent 的权限判决模型,读完后可完整理解 RTK hook 的生命周期管理设计并具备为新增 Agent 编写安装逻辑的能力。

一、模块定位:生命周期管理层,而非重写执行层

src/hooks是整个 RTK 中面向 LLM agent 的 hook 生命周期管理层:负责 hook 的安装、卸载、完整性校验、使用审计与信任管理。一个关键的边界划分是——它创建并维护位于仓库根目录hooks/下的 hook 产物,但自身不执行重写逻辑,重写模式注册表在 discover/registry.rs,命令输出过滤在src/cmds/

该模块明确拥有的职责包括:

  • rtk init安装流程(通过 main.rs#L39-L62 中的AgentTarget枚举驱动);
  • SHA-256 完整性校验与 hook 版本检查;
  • 审计日志分析(rtk hook-audit);
  • rtk rewriteCLI 入口;
  • TOML 过滤器信任管理(rtk trust)。

两条边界说明值得注意(引自 src/hooks/README.md):

  1. rewrite_cmd.rs 是一个薄 CLI 桥——它存在的唯一目的是服务 hook(hook 以子进程方式调用rtk rewrite),并完全委托给discover/registry
  2. trust.rs 控制项目级 TOML 过滤器的执行许可。它放在 hooks 模块而非核心过滤引擎中,因为信任流程与"hook 安装时发现的过滤器"这一场景绑定。

模块的总入口是 src/hooks/mod.rs,其中is_claude_hook_command()discover::lexer::shell_split解析命令,识别rtk hook claude形态(含绝对路径与带引号路径,单元测试见 mod.rs#L26-L45),保证 hook 命令无论以何种路径调用都能被正确识别。

二、核心目的:命令拦截与自动重写

hook 的工作模型是:AI 助手即将执行一条原始 CLI 命令(如git status)时,hook 拦截该命令,将其重写为 RTK 等价形式(如rtk git status),使 LLM agent 自动获得 token 节省收益。整个仓库的 hook 实现分为两类:

  • 安装产物:部署到用户机器上的脚本/插件,位于根目录 hooks/(如 hooks/claude/rtk-rewrite.sh、hooks/opencode/rtk.ts、hooks/pi/rtk.ts);
  • 原生 Rust hook 命令:constants.rs 定义了rtk hook claudertk hook cursorrtk hook droidrtk hook vibe等原生命令,注释明确标注它们"replaces rtk-rewrite.sh",即新版安装直接注册二进制命令而非 shell 脚本。

三、rtk init:安装模式全景

rtk init支持以下安装流程(完整继承自文档,含各模式创建与修补的文件):

模式命令创建修补
Default (global)rtk init -gHook, SHA-256 hash, RTK.mdsettings.json, CLAUDE.md
Hook onlyrtk init -g --hook-onlyHook, SHA-256 hashsettings.json
Claude-MD (legacy)rtk init --claude-md134-line RTK blockCLAUDE.md
Windsurfrtk init -g --agent windsurf.windsurfrules--
Clinertk init --agent cline.clinerules--
Codexrtk init --codexRTK.md in$CODEX_HOMEor~/.codexAGENTS.md
Cursorrtk init -g --agent cursorCursor hookhooks.json
Pirtk init --agent pi.pi/extensions/rtk.ts--
Hermesrtk init --agent hermesPython plugin in~/.hermes/plugins/rtk-rewrite/config.yamlplugins.enabled

AgentTarget 枚举:源码中的实际覆盖面

文档描述安装流程"6 agents viaAgentTargetenum,现包括 Mistral Vibe + 3 个特殊模式(Gemini、Codex、OpenCode)"。从 main.rs#L39-L62 的当前源码看,AgentTarget枚举已扩展为 11 个变体:Claude(默认)、CursorWindsurfClineKilocodeAntigravityKimiPiHermesDroidVibe,说明该枚举是随新 Agent 支持持续增长的注册表,而非固定清单。

init 流程的源码要点

init.rs 中值得关注的实现细节:

  • 嵌入产物:OpenCode 插件与 Pi 扩展通过include_str!在编译期嵌入 hooks/opencode/rtk.ts 与 hooks/pi/rtk.ts,RTK 感知指令则嵌入 hooks/claude/rtk-awareness.md(Claude 与 Codex 各有一份精简版),因此安装不依赖仓库目录存在;
  • 过滤器模板:当目标位置没有filters.toml时,rtk init会写入带注释的模板(FILTERS_TEMPLATE/FILTERS_GLOBAL_TEMPLATE),示例展示了match_commandstrip_lines_matchingmax_lineson_empty等字段用法;
  • 参数冲突校验run()入口先做模式互斥检查,例如--codex不能与--opencode--claude-md--hook-only--auto-patch--no-patch组合;OpenCode、Cursor、Windsurf 模式是 global-only,缺少-g会直接报错并给出正确用法提示(见 init.rs#L278-L308)。

四、完整性校验:防止 hook 被篡改

完整性系统的动机在 integrity.rs#L1-L13 的模块注释中写明:RTK 安装的 PreToolUse hook 会以permissionDecision: "allow"自动批准重写后的命令,绕过了 Claude Code 的权限提示,因此任何未授权的 hook 修改都构成命令注入向量。系统流程分三步:

  1. 安装时integrity::store_hash()计算 hook 文件的 SHA-256,写入~/.claude/hooks/.rtk-hook.sha256(只读 0o444);
  2. 运行时integrity::runtime_check()重新计算哈希并比对,若被篡改则阻止执行;
  3. 按需rtk verify打印详细校验状态(PASS/FAIL/WARN/SKIP)。

五种完整性状态定义在 integrity.rs#L28-L39 的IntegrityStatus枚举中:

状态含义
Verified哈希与存储值一致
Tampered哈希不匹配(阻止执行)
NoBaselinehook 存在但无存储哈希(旧版安装)
NotInstalled无 hook、无哈希
OrphanedHash哈希文件存在但 hook 缺失

源码层面还有两处值得注意的工程取舍(见 integrity.rs#L79-L108 的store_hash):

  • 哈希文件格式兼容sha256sum -c<hex_hash> rtk-rewrite.sh,可用标准工具复核;
  • 0o444 只读权限自述为"speed bump 而非安全边界"——拥有写权限的攻击者可以 chmod 掉它,其价值在于把"意外覆盖"变成"需要刻意为之的动作"。这种对安全模型边界的诚实标注,是阅读该源码时值得借鉴的做法。

五、PatchMode:settings 文件的三种修补策略

rtk init修改 agent 配置文件(如settings.json)时的行为由 PatchMode 控制,定义在 init.rs#L78-L84:

模式标志行为
Ask (default)--提示用户[y/N];stdin 非终端时默认 No
Auto--auto-patch不提示直接修补;面向 CI/脚本化安装
Skip--no-patch打印手工操作说明,由用户自行修补

与修补相关的完整状态由PatchResult枚举表达(init.rs#L94-L102):PatchedAlreadyPresent(hook 已在 settings.json 中)、Declined(用户拒绝提示)、Skipped--no-patch)、WouldPatch(dry-run 下本应添加)。所有 init 子模式共享InitContext { verbose, dry_run }上下文,dry-run 模式只打印"would create / would update"而不触碰文件系统,结束后统一输出[dry-run] Nothing written.尾注——这让安装流程可以在不落盘的情况下被完整验证。

过滤器信任也有对等的三态FilterTrust { Ask, Trust, Skip }(默认 Ask,见 init.rs#L86-L92),与 PatchMode 形成"修补设置文件"与"信任项目过滤器"两条并列的交互式决策链。

六、原子性与幂等性

所有文件操作使用原子写入(tempfile + rename)防止崩溃时产生半截文件;settings 类文件在修改前备份为.bak;全部操作幂等——多次运行rtk init安全无副作用。init.rs 中的write_if_changed()进一步实现了"内容未变则不写盘"(verbose 下打印 already up to date),并对 symlink 目标做了显式处理,保证 rename 落在真实文件上、symlink 本身被保留。

七、权限模型:Deny > Ask > Allow

RTK 强制执行一套与 Claude Code 最小权限默认值对齐的权限优先级:

Deny > Ask > Allow (explicit) > Default (ask)

规则来源与解析规则:

  • 所有Claude Codesettings.json文件加载(项目 + 全局,含.local变体,路径常量见 constants.rs 的SETTINGS_JSON/SETTINGS_LOCAL_JSON);
  • 只提取Bash(...)规则,其他作用域(Read、Write)被忽略。

四种判决到 hook 行为的映射(完整继承自文档):

判决触发条件rewrite_cmd 退出码Hook 行为
Denypermissions.deny规则匹配2Passthrough — 交由宿主工具处理拒绝
Askpermissions.ask规则匹配3重写 + 让宿主工具提示用户
Allowpermissions.allow规则匹配0重写 + 自动放行
Default无规则匹配3重写 + 让宿主工具提示用户

判决逻辑的源码级拆解

permissions.rs 的check_command_with_rules()实现了三条比"简单优先级"更严格的安全规则:

  1. Deny 抢占:任一命令段匹配 deny 规则立即返回Deny,先于一切其他构造;
  2. 不可证伪构造强制 Ask:若命令包含contains_unattestable_construct(如命令替换、文件目标重定向等无法静态证明安全的构造),直接降级为 Ask,永不自动放行
  3. Allow 要求全段匹配:复合命令(&&链)中每一个非空段都必须独立命中 allow 规则才给 Allow,任一段不匹配即失去 Allow 状态。源码注释指明这是 issue #1213 的修复——此前单个段命中 allow 就升级整条链为 Allow,可被用于绕过。

退出码契约与 rewrite_cmd

rewrite_cmd.rs#L12-L17 的文档注释给出了 hook 消费侧看到的完整契约:

退出码stdout含义
0重写结果重写被允许 — hook 可自动放行重写命令
1(空)无 RTK 等价命令 — hook 原样透传
2(空)Deny 规则命中 — 交由 Claude Code 原生拒绝处理
3重写结果Ask 规则命中 — 重写但让 Claude Code 提示用户

hook 中的典型消费方式即文档给出的模式:REWRITTEN=$(rtk rewrite "$CMD") || exit 0。注意rewrite_cmd.rsAllow走正常Ok(())返回,而Ask/Deny/Passthrough通过std::process::exit直接退出——退出码本身就是协议。

各 Agent 的 ask 支持矩阵

工具ask 支持Default 下的行为
Claude Code (rtk-rewrite.sh)YespermissionDecision: "ask"— 提示用户
Copilot VS Code (rtk hook copilot)YespermissionDecision: "ask"— 提示用户
Cursor (rtk hook cursor)Readypermission: "ask"— Cursor 实施该权限后将提示用户;在此之前放行
Gemini CLI (rtk hook gemini)No(仅 allow/deny)allow(限制 — Gemini 无 ask 模式)
Copilot CLI (rtk hook copilot)No updatedInputdeny-with-suggestion(行为不变)
Codexask 被解析但为 no-opallow(限制 — fails open)
Mistral Vibe (rtk hook vibe)无原生 ask 界面passthrough — Vibe 自身对重写命令触发审批提示

对应地,permissions.rs#L34-L41 的Host枚举按宿主加载各自的规则来源:Claude(Claude settings.json)、Cursor.cursor目录)、Gemini.gemini目录)、Droid.factory目录 +FACTORY_HOME_OVERRIDE)、Vibe(当前为空规则集,即完全 passthrough 语义)。

实现分工

  • permissions.rs — 加载 deny/ask/allow 规则、评估优先级、返回PermissionVerdict
  • rewrite_cmd.rs — 将判决映射为退出码(shell hook 消费);
  • hook_cmd.rs — 将判决映射为 JSONpermissionDecision字段(Copilot/Gemini 等 stdin/stdout JSON 协议消费),main.rs#L899-L921 的HookCommands枚举登记了ClaudeCursorGeminiCopilotDroidVibe六个处理器及一个Check干跑入口(rtk hook check --agent <agent> <command>)。

八、非阻塞保证:Exit Code Contract

hook_cmd.rs 中的 hook 处理器必须在每一条路径上都返回Ok(())——成功、无匹配、解析错误乃至意外输入皆然。返回Err会传播到main()导致非零退出,从而阻塞 agent 命令的执行,违反 hooks/README.md 中记载的非阻塞保证。这是 hook 类组件的核心不变量:hook 是旁路加速器,绝不能成为命令执行的故障点。模块上#[deny(clippy::print_stdout, clippy::print_stderr)](见 mod.rs#L6)也侧面印证了该模块对输出通道的严格纪律——JSON 协议处理器不允许有额外输出污染 stdout。

九、配套机制:信任管理、版本检查与审计

文档将信任管理列为本模块职责之一,trust.rs 的模块注释给出了完整的trust-before-load模型:.rtk/filters.toml从 CWD 以最高优先级加载,攻击者可将其提交到公共仓库来控制 LLM 看到的内容(隐藏恶意代码、压制安全扫描输出、用replace/match_output原语改写命令输出)。为此:未信任的过滤器被直接跳过而非"带警告加载";rtk trust在用户审查后存储 SHA-256;内容变化即失效信任(需重新审查);RTK_TRUST_PROJECT_FILTERS=1供 CI 流水线覆盖。

另外两个文档点名的能力在源码中同样可查证:

  • hook 版本检查:hook_check.rs 中CURRENT_HOOK_VERSION = 3maybe_warn()每 24 小时至多提示一次,HookStatus枚举区分Ok/Outdated/Missing,并能识别"新二进制 hook 已注册但旧脚本未清理"的半迁移状态,提示用户重跑rtk init -g清理;
  • 审计rtk hook-audit --since N(默认 7 天)展示 hook 重写指标,前提是环境变量RTK_HOOK_AUDIT=1(见 main.rs#L869-L875 的命令定义与 hook_audit_cmd.rs)。

十、开发指南:接入一个新的 AI 编程 Agent

文档给出的四步扩展流程与源码结构一一对应,是贡献该模块的标准路径:

  1. 安装逻辑:在 init.rs 中按现有 agent 模式添加安装函数(可参考 embeddedinclude_str!插件与write_if_changed原子写入的既有写法);
  2. 自定义 hook 协议处理器:若 agent 需要专属协议(如 Gemini 的BeforeTool或 Vibe 的pre_tool事件键,常量见 constants.rs 的PRE_TOOL_USE_KEY/BEFORE_TOOL_KEY),在 hook_cmd.rs 添加处理器函数,并在 main.rs 中同步添加HookCommands::<Agent>变体与AgentTarget::<Agent>枚举项;
  3. 权限面接线:若 agent 有可安装的权限面(denylist/allowlist),在 permissions.rs 的check_command_for()中通过新的Host::<Agent>变体接入;
  4. 完整性基线:在 integrity.rs 中为新 hook 文件登记期望哈希。

两条注意事项:hook_check.rs::maybe_warn()目前只检查 Claude Code hook,其他 agent 没有过期 hook 的警告路径;验证方式是在全新环境中运行rtk init,然后在目标 agent 中确认 hook 能正确重写命令。

十一、相关文件索引

文件职责
src/hooks/mod.rs模块入口、is_claude_hook_command识别
src/hooks/init.rs各 agent 安装流程、PatchMode、dry-run、模板
src/hooks/integrity.rsSHA-256 存储/校验、五态IntegrityStatus
src/hooks/permissions.rsdeny/ask/allow 规则加载与判决
src/hooks/rewrite_cmd.rsrtk rewrite退出码契约
src/hooks/hook_cmd.rs各 agent 的 JSON 协议处理器
src/hooks/hook_check.rshook 版本检查与过期警告
src/hooks/verify_cmd.rsrtk verify:TOML 过滤器内联测试执行
src/hooks/trust.rs项目过滤器信任存储
src/hooks/hook_audit_cmd.rshook 重写审计指标
src/hooks/constants.rs目录、事件键、hook 命令常量
hooks/README.md部署产物(安装脚本/插件)总览
docs/contributing/TECHNICAL.md整体架构参考

需要说明的适用前提:本文以当前仓库src/hooks的源码状态为准;AgentTarget枚举成员、Host变体与各 agent 的 ask 支持矩阵会随新 Agent 接入而变化,接入新 agent 前建议先核对 main.rs 与 permissions.rs 的最新定义。

【免费下载链接】rtkCLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies项目地址: https://gitcode.com/GitHub_Trending/rtk4/rtk

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询