mcpField
G
GVNR by Instruxi

GVNR by Instruxi

本地服务
gvnr.io安全04 周前更新

关于 GVNR by Instruxi

GVNR by Instruxi 是 Claude Code 插件,可在 Agent 操作执行前按预算、循环状态和能力规则作出允许、改写、拒绝或询问决定,并记录可验证的决策链。连接 Enforcer 后还可应用组织策略。

主要功能

  1. 01执行前检查 Agent 操作
  2. 02按预算和速率限制决策
  3. 03拦截或改写危险命令
  4. 04应用 Enforcer 组织策略
  5. 05生成哈希链审计记录

适用场景

  1. 01限制编码 Agent 的支出
  2. 02在危险命令执行前要求确认
  3. 03审计 Agent 操作决策

MCP 相关

GVNR 插件本身并不附带 MCP 服务;Enforcer 插件提供 Enforcer MCP 服务,与 Governor 共用登录凭据。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"io-gvnr-enforcer-governor": {
"command": "npx",
"args": [
"-y",
"enforcer-governor"
],
"env": {
"GOVERNOR_PORT": "<GOVERNOR_PORT>",
"GVNR_AGENT": "<GVNR_AGENT>"
}
}
}
}
启动命令npx -y enforcer-governor
需要配置的环境变量
GOVERNOR_PORTPort where GVNR is running (npx enforcer-governor start). Defaults to 4000.
GVNR_AGENTA name for this client's agent, shown as mcp:<name>. Defaults to a random id per session.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmenforcer-governor0.30.0

工具

5

收录的工具列表 · 更新于 2 周前

参数类型必填说明
actionstring是即将执行的具体操作:完整的 shell 命令;如果是文件操作,则提供动词和路径,例如 "write /app/.env"。

详细介绍

Enforcer Governor

一款 Claude Code 插件,可在 Agent 执行动作之前判断是否允许执行,并留下可供验证的记录。

Agent 会陷入循环、重复工作,还会直接运行来自互联网的脚本。其他工具都是事后才报告损害。这款工具会先回答一个问题:

此刻允许这个 Agent 执行此操作吗?

  • allow — 未超预算且符合当前任务,继续执行
  • rewrite — 以符合策略的形式执行
  • deny — 超出预算、陷入循环,或完全不允许
  • ask — 暂停并征求你的确认

每个决定都会追加到一份哈希链式记录中。只要编辑或删除其中一行,链条就会明显断裂。在登录 Enforcer 工作区之前,所有内容都会留在你的设备上——请参阅哪些内容会离开你的设备。

安装

/plugin marketplace add instruxi-io/claude-plugins
/plugin install enforcer@instruxi
/plugin install enforcer-governor@instruxi

然后重启 Claude Code,并使用 /enforcer:login 登录一次。enforcer 插件负责连接到你的 Enforcer 工作区(包括其 MCP 服务和登录功能);governor 无需它也能运行,在本地做出决定并记录,但没有租户策略可供查询,也无处发送回执。

governor 提供强制执行功能:钩子、能力规则和八个斜杠命令。执行 /plugin disable enforcer-governor 即可同样彻底地移除这些功能。无需启动守护进程。governor 会将自身状态保存在 ~/.enforcer-governor/ 下,并在 ~/.enforcer/ 下保存一个小型共享文件;只有在你使用 /enforcer-governor:telemetry on 明确要求时,它才会写入你的 ~/.claude/settings.json。

有两项内容无法通过这种方式安装,因为 Claude Code 不允许插件打包其中任何一项:插件的 settings.json 只接受 agent 和 subagentStatusLine 键,其他内容都会被忽略,且不会给出提示。这两项都只需粘贴到你自己的 ~/.claude/settings.json 中;即使不添加其中任何一项,governor 也能正确执行强制策略。

状态栏是唯一始终显示的界面——你扫一眼就能看到的数字,无需打开其他内容:

{
  "statusLine": {
    "type": "command",
    "command": "node \"$(cat \"$HOME/.enforcer/plugin-root\")/statusline/spend.mjs\""
  }
}

插件的安装目录会随每个版本变化,因此状态栏不会写死该目录:governor 会在每次会话开始时,将自身运行位置记录到 ~/.enforcer/plugin-root,命令会根据该记录定位。安装后首次启动会话之前,状态栏不会显示任何内容。

权限规则为钩子提供额外保障。将此仓库的 settings.json 中的 permissions 块复制到你的配置中。插件无法自动安装这部分内容;如果不粘贴,钩子就只能独自处理——其具体影响请参阅工作原理,实际影响没有听起来那么大。

你将获得什么

状态栏中的一个数字,也是金额数据的来源。 · $3.40/$20 — 实时显示相对于限额的支出,无需打开仪表板。需要你处理问题时,它会改变一次颜色。

它还有第二个作用。Claude Code 会把自己的 total_cost_usd 传给状态栏,仅传给状态栏——钩子永远看不到这个值。该金额可能按组织签约费率计算,因此比任何第三方价格表都更准确。状态栏会记录此值,门控会在下次工具调用时读取。即使不粘贴 安装 中的配置,governor 仍会执行强制策略;只是会改为根据对话记录计量,而且每份回执都会注明采用了哪种数据。

插件随附的能力规则。 直接将 URL 内容管道传入 shell 会被直接拒绝。删除目录树、重写 git 历史、读取凭据、发布或部署:这些操作都会暂停并征求确认。这些是能力判断,不涉及支出,因此即使预算已用满也会触发;而且检查时完全不会读取任何状态,因此即使 governor 无法读取自己的文件,这些规则仍然有效。删除状态目录会关闭支出限额,但不会关闭这些规则。 这是你所在组织的策略,不只是我们的策略。 通过 /enforcer:login 登录一次后,上述规则就不再是对所有人都相同的六种模式。规则匹配时,治理器会就此询问你的 Enforcer 租户策略:一个类型为 agent_action、ID 对应规则名称(fs.delete_tree、deploy.publish、git.force_push、git.rewrite_history、secrets.access、shell.pipe_to_shell)的资源。策略采用 Rego 编写,按版本管理,上线前经过测试,并通过激活上一版本回滚。因此,“这里的 Agent 绝不删除整个目录树”或“发布需要人工确认”只需更改一次,全团队即可生效。

策略的回答会与本地规则组合,且处理方向很重要:

本地拒绝本地询问本地改写
策略拒绝拒绝按策略措辞拒绝拒绝
策略 ask: 原因拒绝按策略措辞询问询问
策略允许拒绝无异议改写
无规则 / 无法访问拒绝询问改写

策略可以让任何规则更严格,也可以免除确认。它不能解除硬性拒绝(curl | sh 仍会被拒绝),也不能跳过改写。如果 Enforcer 响应缓慢、服务中断或你已退出登录,则仅由本地规则决定,因此网络中断绝不会让更多操作获准。只有匹配的命令才会被询问;普通工具调用不会等待网络响应。回答会缓存 policyTtlSec(30s),这也决定了新策略版本多久能同步到每台机器。

declared_types := ["agent_action"]

allow if { input.resource_type == "agent_action" }   # declared, so silence would deny

deny contains "agents in this tenant do not delete whole directory trees" if {
	input.resource_type == "agent_action"
	input.resource.id == "fs.delete_tree"
}

deny contains "ask: publishing from an agent needs a person to confirm" if {
	input.resource_type == "agent_action"
	input.resource.id == "deploy.publish"
}

治理器和 Enforcer MCP 服务只需登录一次。 MCP 服务随同一市场中的 enforcer 插件一起提供(/plugin install enforcer@instruxi),而不包含在治理器中;在 2.6.0 之前,治理器自带一份副本。两者读取同一份凭据 ~/.enforcer/credentials.json(0600),因此 /enforcer:login 和 /enforcer-governor:login 是同一次登录:登录一次,两者都会登录;退出一次,两者都会停止发送凭据。

以美元计价的支出上限。 /enforcer-governor:limit 40 为每个 Agent 设置 $40 上限。Claude 按各模型自身的费率计价,因此无论 Agent 使用 Opus 5 还是 Haiku 4.5,$40 都是 $40。如果无法获取运行环境提供的数据,上限就会改用按成本加权的有效令牌数,因为缓存会话每轮都会重新读取完整上下文,原始令牌数会激增,但费用却很低。

让 Agent 知道,而不只是让你知道。 得知自己接近上限的 Agent 可以先完成手头工作,而不是另开战线。预算消耗约达到三分之二时,系统会在回合边界告知一次——只用一句话,此时 Agent 仍能调整计划——然后治理器会保持安静,直到情况发生变化。等到触及上限才警告就太迟了:当前回合已经开始。每句话都有成本,因为它会加入缓存前缀,并在之后的每个回合重复计费;这就是为什么只说一句,而且只说一次。

用更安全的命令替代被拒绝的命令。 有些操作有一种既保留意图又去除风险的形式。git push --force 会改为 git push --force-with-lease;只有在你上次获取后有人推送过内容时,该命令才会拒绝执行——这正是会导致工作丢失的情况。改写会被告知并记录;悄悄编辑命令的治理器会成为对话记录中一个隐形的操作者。

速率限制,而不只是总量限制。 真正造成大额开支的事故,都是速率方面的事故。每分钟美元支出、新增 Agent 数量和错误数都会分别受到监控,并且每项都只会询问,而不会阻止操作——且只询问一次,因此夜间运行会等待你的确认,而不是失败或反复提醒。

可供审计读取的记录。 /enforcer-governor:verify 会遍历文件,并指出第一处对不上的行。每张回执都记录了 Agent 代表谁执行操作、尝试了什么、由哪个模型响应,以及由哪条规则作出决定。

命令

/enforcer-governor:status查看每个 Agent 的支出、限额、当前消耗和记录状态
/enforcer-governor:verify检查链条,并指出第一处损坏的记录
/enforcer-governor:limit <dollars>设置每个 Agent 的限额
/enforcer-governor:resume [agent]重新运行已停止的 Agent,并为其留出完成任务的空间
/enforcer-governor:config [--why]查看每项设置、其作用,以及你修改过的设置
/enforcer-governor:set <name> <value>更改一项设置,并进行验证
/enforcer-governor:login [<WORKSPACE-CODE> | api-key <key> | status | logout]登录 Enforcer,与 /enforcer:login 使用相同的登录方式;不带参数会打开浏览器,工作区代码可跳过输入提示
/enforcer-governor:telemetry [on | off | status]将 Claude Code 自身的 OpenTelemetry 数据(费用、令牌、工具使用情况——绝不包括提示词文本)发送到你的 Enforcer 工作区;将 OTEL_* 导出器设置写入 ~/.claude/settings.json
暂时解除限制

因支出而停止的 Agent,可通过提高限额来解除限制——/enforcer-governor:limit 40——然后从停止的地方继续运行。因循环执行而停止的 Agent,或由你手动停止的 Agent,则需要使用 /enforcer-governor:resume,因为提高限额并不代表同意它继续重复执行相同的操作。

若要在不卸载的情况下关闭 governor,请将以下任意内容写入 ~/.enforcer-governor/config.json:

{"budgetOn": false, "loopOn": false, "rulesOn": false}

budgetOn 控制支出和速率检查,loopOn 控制循环检查,rulesOn 控制能力规则。这三项相互独立:关闭支出跟踪后,curl | sh 和 rm -rf 仍会受到保护。三项全部关闭后,功能将完全停用——除非你的组织发布了下文所述的底线设置。不要删除 state.json 来解除卡住的状态:它保存着收据链的链头,因此下一张收据会在没有前一张收据的情况下进行哈希计算,verify 也会正确地报告记录已损坏。

组织设置

Enforcer 租户可以为每个登录的安装实例发布一组此类设置(GET /api/v1/governance/settings,由租户管理员写入)。这些设置是底线,而非覆盖值:对于每项设置,governor 都会采用两者中更严格的那个,因此受管理的 $150 限额会优先于你的 $400,而你的 $40 则会优先于两者;组织启用的检查也无法在本地关闭。/enforcer-governor:config 会用 ! 标记这些设置,并在旁边显示你自己的值。

无需等待网络响应来决定:设置会在会话开始时获取,此后最多每小时获取一次,并进行缓存。已退出登录或从未连接过控制平面的机器,只使用自己的配置运行。身份设置(centralUrl、ingestUrl、operator)不可由组织管理——能够重新指定安装实例的目标,就能重定向其收据。

会离开你设备的内容

在你登录之前,不会有任何内容离开。治理器会在本地做出决定并记录;从未登录过的设备(/enforcer:login)不会发起任何网络请求。

登录 Enforcer 工作区后,以下内容可能会离开设备,且每项都可通过开关控制:

内容发送时机开关
决策回执 — 裁定结果、触发的规则、工具名称、模型、令牌数量、根据工作目录推导出的项目名称、你设置的 operator,以及做出决定的框架(claude-code)及其适配器版本。绝不包含命令文本、文件内容或提示词。每个会话结束后在后台发送到工作区的治理 APIshipOn(默认开启)
会话的项目 — 会话开始时发送回执使用的会话 ID(claude: + 8 个字符)和根据工作目录推导出的项目名称,以便将会话归档到对应项目下,即使该会话从未做出受治理的决定。不会发送其他内容。每个会话开始时发送一次简短请求,绝不重试shipOn(默认开启)
你所在组织的策略答复 — 当本地规则与某项操作匹配时,治理器会询问你的工作区应允许、询问还是拒绝。请求中会指出规则,而不是命令。仅在规则匹配时policyOn(默认开启)
Claude Code 自身的遥测数据 — 来自 Claude Code 内置 OpenTelemetry 导出器的费用、令牌和工具使用指标。不会导出提示词文本。仅在你启用后/enforcer-governor:telemetry on(默认关闭)

所有内容只会发送到你登录的工作区,不会发送到其他地方。/enforcer:login logout(或 /enforcer-governor:login logout,是同一个命令)会停止此设备上的所有发送;已发送的内容仍会保留在工作区记录中,这正是记录的意义所在。

治理器写入的文件:~/.enforcer-governor/(配置、状态、回执链,以及会在 sweepDays 后清理的会话临时文件)、~/.enforcer/credentials.json(你的登录信息)和 ~/.enforcer/plugin-root(已安装插件的位置,因此遥测在插件更新后仍会保持登录状态)。

决策及其代码

治理器作出的每项决策,包括“无异议”,都是一条 JSON 记录:

{"decision":"deny","code":"push_not_alone","rule":"graph.push","tool":"Bash","summary":"a push, pull request or land must be the whole command, on its own","run_id":"a54645af-…"}
  • decision 为 allow、deny 或 ask(改写内容会作为携带更安全命令的 ask 提交给用户)。
  • code 是以下代码之一,在 core/codes.mjs 中定义一次。代码是一套公开的词汇:只会新增,不会重命名或重复使用。
  • rule 是规则的政策 ID(git.force_push),如果没有规则作出决策,则为 null。
  • 只有当会话中有 enforcer-graph 运行(ENFORCER_GRAPH_RUN_ID,或 enforcer-graph 的钩子保留的运行文件)时,才会出现 run_id。

此记录会写入三个位置:作为 decision 字段,写在 ~/.enforcer-governor/receipts.jsonl 中哈希链回执的末尾;作为一行写入 PreToolUse 钩子的 stderr,并以 enforcer-governor:decision 为前缀;以及作为权限理由的第一行,只要钩子允许、拒绝或询问,就会写入该行。父进程(图调度器、CI 包装器)会解析这一行,而不是从下方的句子中搜索。

代码含义
pipe_to_shell将通过 curl/wget 获取的脚本传入 shell
force_push强制推送(--force、-f 或 +refspec)
destructive_delete对整个目录树执行 rm -rf
destructive_git改写历史:reset --hard 或 filter-branch
secret_in_command操作会读取或写入凭据(.env、密钥、credentials.json、/.aws、/.ssh)
deploy_publish发布或部署(npm publish、vercel --prod、kubectl apply/delete、terraform apply)
custom_rule来自 config.json 且没有专属代码的规则
graph_push_allowed无头工作器将自己的 graph/ 分支作为完整命令推送
graph_pr_allowed无头工作器从自己的 graph/ 分支创建拉取请求
graph_land_allowed无头工作器使用 land-pr.sh 合并自己的 graph/ 拉取请求
graph_push_confirm有人在场的会话中推送 graph/:由其确认
graph_pr_confirm有人在场的会话中,从 graph/ 分支创建拉取请求
graph_land_confirm有人在场的会话中执行 land-pr.sh
push_not_alone推送、拉取请求或合并操作与其他命令串联;必须单独运行
push_default_branch推送到默认分支(main、master、develop、trunk)
push_needs_approval_surface会话中无人可供询问,且没有规则允许此推送或拉取请求
branch_mismatch推送的分支不是工作树当前检出的分支
outside_worktree工作目录不是位于 graph/ 分支上的 git 工作树
governor_settings_edit修改插件或治理器设置
tenant_policy由组织的 Enforcer 政策作出决策
agent_stoppedAgent 已被用户停止或因循环而停止;恢复前会保持停止状态
period_limit已达到每日、每周或每月支出限额
loop_detectedAgent 超过循环限额,重复执行同一操作
burn_rate支出速度超过每分钟限额
fanout_rate启动子 Agent 的速度超过扇出限额
retry_storm失败和重试的速度超过重试限额
client_limit已达到客户端的支出限额
spend_limit已达到 Agent 的支出限额
spend_warningAgent 的支出已超过限额中的警告阈值
no_rule_matched没有规则提出异议,且支出在限额内
spend_unchecked没有规则提出异议;治理器无法读取其状态,因此未检查支出
checks_off没有规则提出异议;支出和循环检查均已关闭
无头图工作器

图工作器是一个 claude -p 会话,调度器会在 graph/<key> 分支的 git 工作树中启动它,且没有人可以回答提示词。Governor 是允许或拒绝其交付步骤的插件(core/worker.mjs);jev-hooks 和 enforcer-graph 不再决定这些步骤。当设置了 JEV_HOOKS_HEADLESS=1 或 ENFORCER_HEADLESS=1,或者 Claude Code 报告入口点为 sdk-cli(claude -p)时,会话即为无头。分支从命令运行所在目录的 .git 中读取(cwd,或 git -C <dir>)。

规则无头有人在场
graph.push:在该分支上执行完整命令 git push -u origin graph/<key>(也包括 HEAD:graph/<key>,以及 rebase 路径中的 --force-with-lease)允许 graph_push_allowed;命令串联:拒绝 push_not_alone;其他分支:拒绝 branch_mismatch;不在图工作树中:拒绝 outside_worktree;其他任何 push:拒绝 push_needs_approval_surface询问 graph_push_confirm
graph.pr_create:从图分支执行 gh pr create允许 graph_pr_allowed;不在图分支上:拒绝 outside_worktree询问 graph_pr_confirm
graph.land:land-pr.sh,包括技能中的 "$(ls -d …/land-pr.sh | tail -1)" 形式允许 graph_land_allowed;不在图分支上:拒绝 outside_worktree询问 graph_land_confirm
git.force_push:--force、-f、+refspec拒绝 force_push改写为 --force-with-lease(需询问)force_push
git.push_default_branch:推送到 main、master、develop 或 trunk拒绝 push_default_branch询问 push_default_branch
governor.settings:编辑 .claude/settings*.json、managed-settings.json、.claude/plugins/ 或 ~/.enforcer-governor/(Edit/Write,或会写入文件的 shell 命令)拒绝 governor_settings_edit询问 governor_settings_edit

这些是唯一会允许的规则(明确授权并跳过提示词)。它们会在能力规则之前运行;租户策略仍可拒绝它们允许的操作;将 rulesOn: false 设为关闭时,也会一并关闭这些规则。

工作原理

prompt  ─► UserPromptSubmit ─► a word to the agent, if the situation changed

                         ┌─ capability rules ─── no state, FAILS CLOSED
                         │     └─ matched? ask your tenant policy (Enforcer)
tool call ─► PreToolUse ─┤         unreachable leaves the local rule in place
                         └─ spend + rate ─────── needs state, FAILS OPEN
                                   │
                                   ▼
             allow / deny / ask / rewrite  ─►  hash-chained receipt

result  ─► PostToolUse ─► what actually happened (errors feed the retry check)
subagent─► SubagentStart ─► fan-out, counted rather than inferred

七个钩子,没有守护进程、没有端口,也没有任何监听进程。状态保存在 ~/.enforcer-governor/ 中,并由锁保护,因此并行工具调用会按判定顺序写入记录。

这种分工就是整体架构,而且两部分会故意朝相反方向失效。支出检查默认放行:如果无法读取状态,钩子就会允许操作,并在原因行中说明未执行检查,因为一个会因自身状态文件缺失而阻止实际工作的 Governor,在更重要的事情上已经失职,而不只是没能执行策略。能力检查默认拒绝,因为它可以这样做——规则是针对操作文本匹配的模式,不需要状态,因此状态目录无法读取不会影响它们。以这种方式判定的拒绝仍会写入记录,但会刻意省略哈希:没有可读取的链尾可供哈希比对,而 verify 会将未哈希的行计为无法验证,而不是链断裂。不记录会掩盖真实的拒绝,而伪造链接则会让正常文件被误报为遭到篡改。

转录内容会从上次调用停止的位置向前读取,因此检查成本不会随着会话时长增加。

运行测试

npm test

更大的图景

Governor 是将一个理念应用于一种资源。这个理念就是 Enforcer,Instruxi 的策略引擎,它会在每个受保护的系统前提出同一个问题:此身份此刻是否可以执行此操作?——并以三种方式给出答案,每个答案都有防篡改回执。在这里,身份是 AI Agent,资源是你的资金。instruxi.io

许可证

Functional Source License 1.1(FSL-1.1-ALv2)。可免费用于你自己的 Agent,包括生产环境和商业用途。不得将其作为竞争性的商业产品或服务提供。每个版本发布两年后转为 Apache 2.0。详见 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。