New:Microsoft Teams Notifications Are Now Available in Socket.Learn more →
Get Started

enforcer-governor

Package Overview
Dependencies
Maintainers
1
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

enforcer-governor

GVNR: stops AI agents overspending and running dangerous commands, before the action runs. Per-minute spend caps, allow/deny/escalate on every action, hash-chained receipts, MCP server. Free, self-hosted. Formerly Enforcer Governor.

latest
Source
npmnpm
Version
0.30.0
Version published
Maintainers
1
Created
Source

GVNR

Published on npm as enforcer-governor, and previously called Enforcer Governor.

gvnr.io · stops AI agents overspending and running dangerous commands, before the action runs. Free, self-hosted, and an MCP server for any MCP client.

ci npm downloads licence FSL-1.1-ALv2 node

The GVNR dashboard: each agent's task and spend against its cap, with the hash-chained receipt strip underneath

The dashboard, running the live demo with a simulated fleet. Every agent shows what it was asked to do, what it has spent against its cap, and how long it can keep working. Each decision at the bottom is hash-chained to the one before it.

A guard that sits in front of an AI agent and decides, before each action runs, whether it is allowed. Any agent, any provider, on your own machine.

AI agents burn tokens, and tokens are money. They get stuck in loops, repeat work, and blow through budgets, and most tools only report the damage afterwards. GVNR is a guard that stands in front of your agents and answers one question before every action:

May this agent do this, right now?

  • allow: in budget and on task, carry on
  • deny: over its spend cap, stuck in a loop, or a refused command, blocked
  • escalate: irreversible, or spending too fast, so it pauses and asks YOU

Every decision leaves a tamper-evident receipt, so you can always prove what your agents did and who approved what. Everything runs on your own computer with your own keys. Nothing is sent to us, ever.

See it in 10 seconds (no install, simulated agents): https://gvnr.io/console.html

Licensed FSL-1.1-ALv2: free to run on your own agents, in production, commercially. Becomes Apache 2.0 two years after each release. Source-available rather than OSI open source.

Where this sits

Spend caps are no longer unique, and it would be dishonest to imply otherwise. Model providers ship usage limits at organisation, workspace and member level, and hosted services stop spend before the call, some of them by the minute.

Two kinds of tool get close, and each covers half. Spend tools stop the money but have no opinion on what the agent is doing. Command guards block the dangerous command but never look at the money. GVNR does both in one local install, and keeps a receipt of every decision:

Stops spend firstGates the action
Provider limitsat a set totalno
Observability toolsno, reports afterno
LLM gatewaysper key, rate configurableno
Hosted spend capsyes, some by the minuteno
Command guardsnoyes, before it runs
GVNRper agent and fleet, rate limits on by defaultyes, before it runs

GVNR runs on your machine and writes a hash-chained receipt for every decision. The action gate is the Claude Code hook.

A spend cap answers "can it afford this?". It has no opinion on curl | sh, on rm -rf, or on reading your .env, all of which are cheap. A command guard answers "is this dangerous?" and never notices a session fanning out into fifty subagents at $400 a minute. GVNR asks both, on the same action, before it runs.

What you need

One thing: Node.js, a free tool most developers already have. Check by typing node --version in a terminal. If that fails, install it from nodejs.org (big green button, two clicks).

Four things, not one

Control. What the agent may do, checked before it does it. Piping the internet into a shell is refused outright. Deleting a tree, rewriting git history, reading credentials, publishing or deploying: those stop and ask you. Ordinary work passes untouched. These are capability decisions, not spend ones, so they fire with a full budget.

Receipts. Every decision is hash-chained, and each one names the human the agent was acting for, the tool it tried to use, the model answering, and the rule that decided. Edit or delete a single record and the chain visibly breaks. Those are the fields an audit asks for, recorded as fields rather than buried in prose.

Spend. A dollar limit per agent, a soft cap that asks you before it keeps going, and total caps across every agent per day, week and month. Loops and repeated work are caught on behaviour, not just cost.

Speed, not just totals. The incidents that actually cost people money are rate incidents, and most caps that ship elsewhere are totals. Three shapes are watched:

  • Dollars per minute, per agent and across the fleet. Ordinary work sits around $0.10 to $0.25 a minute, so the defaults of $2 and $10 leave normal sessions alone.
  • New agents per minute. A team that starts over an hour is a choice. Eight appearing inside a minute is an orchestrator spawning orchestrators, which is how one documented session reached 49 subagents before anyone looked.
  • Errors per minute. A rate-limited call fails cheaply; the retry after it does not. One report had 96% of attempts coming back rate limited while the wrapper kept paying for the rest.

Each one asks rather than blocks, and asks once, so an overnight run stops and waits for you instead of dying or nagging.

It does not claim to detect hallucination. Nobody can do that reliably. It catches the mechanical waste that is actually detectable, and escalates the judgment calls to you.

Start it (one command)

Open a terminal and run:

npx --yes enforcer-governor start

The first run downloads it (a few seconds), then your dashboard opens in the browser by itself. Leave this terminal running; it is the guard. The dashboard tells you what to do next. Stop it any time with Ctrl+C, and your agents keep working normally.

Route one: point any agent at it

The general case. Anything that talks to an API, on any provider, in any language.

# OpenAI-shaped agents (also Gemini, Grok, Groq, Together, most local runtimes)
export OPENAI_BASE_URL=http://localhost:4000/v1

# Anthropic-shaped agents
export ANTHROPIC_BASE_URL=http://localhost:4000

Every request now passes through GVNR. It meters real usage from each response and refuses (HTTP 429) once an agent is over budget or grounded. Tag requests per agent with an x-enforcer-agent: <name> header so they show up separately on the dashboard.

For anything that speaks the OpenAI chat-completions shape but lives elsewhere, tell GVNR where to forward:

# Gemini
GOVERNOR_OPENAI_URL=https://generativelanguage.googleapis.com/v1beta/openai/chat/completions \
  npx --yes enforcer-governor start

# Grok
GOVERNOR_OPENAI_URL=https://api.x.ai/v1/chat/completions npx --yes enforcer-governor start

# a local runtime
GOVERNOR_OPENAI_URL=http://localhost:11434/v1/chat/completions npx --yes enforcer-governor start

# OpenRouter
GOVERNOR_OPENAI_URL=https://openrouter.ai/api/v1/chat/completions npx --yes enforcer-governor start

Route two: a coding agent with hook support

Some coding agents let a tool inspect an action before it runs. Where that exists it is the stronger route, because a refused action never executes at all rather than being refused at the API, and it catches actions that cost nothing (rm -rf, reading a .env) which an API-level guard never sees. Claude Code is the one wired today.

In a second terminal, go into the project you want watched and run:

npx --yes enforcer-governor install-hook

Then start a new session in that project. That is all. Add --global to watch every project at once. To remove it, press Remove GVNR at the bottom of the dashboard, or run npx enforcer-governor uninstall-hook.

The public ChatGPT website is closed and cannot be governed. Anything built on the OpenAI API can.

Use it from any MCP client

GVNR is also an MCP server, so Cursor, Claude Desktop, Claude Code, Windsurf or any other MCP client can use it. Start GVNR, then add the server:

npx --yes enforcer-governor start          # GVNR and its dashboard
claude mcp add gvnr -- npx -y enforcer-governor mcp

Or in any client's MCP config:

{ "mcpServers": { "gvnr": { "command": "npx", "args": ["-y", "enforcer-governor", "mcp"] } } }
ToolWhat it does
gvnr_request_permissionAsk before acting. Returns allow, deny or ask_human, and records a receipt.
gvnr_statusEvery agent, its spend and spend rate, and the limits in force.
gvnr_recent_decisionsThe latest decisions from the receipt file.
gvnr_verify_receiptsChecks the hash chain and names the line if a record was edited.
gvnr_stop_agentStops an agent. Only a human can resume it, from the dashboard.

Each server process is one agent, shown as mcp:<name>: set GVNR_AGENT in the client config to name it, otherwise it gets a random id per session. The agent cannot pick or change its own id.

Two things to know. Asking permission over MCP is cooperative: an agent that calls the tool gets a real verdict, but nothing forces an agent to call it, so the Claude Code hook remains the enforced route. And nothing in the tool list can loosen a limit: no approve, resume or config, because the agent calling the tools is the one being governed.

Listed on the official MCP Registry as io.gvnr/enforcer-governor.

Configure

Drop a governor.config.json in the directory you run it from:

{
  "dollars": 20,
  "model": "claude-opus-5",
  "soft": 0.75,
  "loopLimit": 4,
  "softAction": "escalate",
  "burnLimit": 2,
  "fleetBurnLimit": 10,
  "fanoutLimit": 8,
  "retryLimit": 6,
  "port": 4000
}

Set the limit in dollars. dollars is the spend cap per agent per session; model is which model's prices convert it into a token budget. The dashboard shows you what that buys before anything runs: how many tokens, and roughly how long an agent can work on it. Change it there at any time; agents already running pick up the new limit immediately, and one that was stopped for hitting the old limit is released.

Claude, OpenAI (GPT) models, Gemini and Grok are all supported, and pricing follows the model automatically. Every agent reports which model answered, so GVNR prices each one at its own rate and the dashboard's price picker follows whatever it sees. $20 means $20 whether that agent is on Opus 5, GPT-5 mini or Gemini 2.5 Pro. Set the pricing by hand and your choice sticks.

Under the hood the cap is cost-weighted effective tokens, not raw counts. Cached sessions re-read their whole context every turn, so raw sums explode into the billions while costing very little. GVNR weights by price instead, so one effective token is one input-token of cost at that model's price and dollars converts with a single multiply.

The weights are per model, because the output multiplier is not a constant: Anthropic prices output at 5x input across its range, OpenAI runs 4x to 8x, and Gemini runs 4x to 8.33x. Cached input differs too. Two Gemini caveats are baked in: the Flash 3.7/3.6 rates are the ones in force through 2026-12-31, and the Pro rates are the sub-200k-prompt tier. $20 is 4,000,000 effective tokens on Opus 5, 2,000,000 on Fable 5 and 10,000,000 on Sonnet 5. Set budget directly instead if you would rather think in tokens.

Prices are the providers' published list rates. On a flat subscription you are not billed per token, so read the dollar figures as equivalent API cost rather than an invoice. When an API key is present in the environment the same work may be billing per token instead of against your plan, which is where the nastiest surprise bills come from, so the dashboard flags that agent rather than leaving you to find out on the invoice.

softAction is "escalate" (ask a human) or "deny" (auto-block at the soft cap). Everything is also flippable live from the dashboard switches.

Optional: a fallback endpoint for when the budget runs out

Off unless you turn it on. When an agent on the API route hits its cap, GVNR denies the request. If you would rather that job carried on somewhere cheaper, such as a local model you run, you can name that endpoint yourself:

{
  "rerouteOn": true,
  "fallbackUrl": "http://127.0.0.1:1234/v1/chat/completions",
  "fallbackModel": "qwen/qwen3.6-27b"
}

GVNR never chooses the endpoint or the model. It sends to exactly the URL and model name you configured, and only after the cap has denied the agent. The denial is still recorded, the forwarded request gets its own receipt, and the response carries x-enforcer-verdict: reroute so your code can tell which endpoint answered.

It is not a context transfer. Resending a long transcript to a new endpoint is the expensive move, because the prompt cache belongs to the old provider. Instead the outgoing model writes a short structured brief, and the fallback starts from that: small enough for any window, including a local one.

Two rules come with it. The brief is written by the outgoing model, because it did the reasoning. And the cut lands on a user turn, so an assistant tool call is never separated from its tool result. It happens once per agent, because repeated summarising distorts earlier reasoning, so a second breach after that is a plain stop.

Measured against a local Qwen 3.6 27B on the same task, scored on a fixed checklist written before the runs:

givenscore
the full transcript6/7
the brief alone7/7
nothing0/7

Three things worth knowing before you turn it on. A local model is slow, so this waits: eight minutes for the run above. max_tokens counts reasoning on a reasoning model, so a brief asked for with a small budget can come back empty. And a brief under 80 characters is refused rather than handed over, so the agent gets the normal denial instead.

Working for more than one client

An agency running five projects needs spend split by client, and the honest problem with that is labelling: nobody tags every session reliably, and the one they forget is the one they cannot bill.

So it is derived. Claude Code tells the hook its working directory on every call, and work for a client almost always lives in that client's folder. Map each folder once:

{
  "clients": {
    "/Users/me/work/acme": "Acme Corp",
    "/Users/me/work/beta": "Beta Ltd"
  },
  "clientLimits": { "Beta Ltd": 400 }
}

Every session in those folders is attributed from then on with nobody typing anything. The longest matching prefix wins, so ~/work/acme/api is Acme even when ~/work is mapped to something else. On the API route there is no working directory, so one header does the same job: x-enforcer-client: Acme Corp.

A folder you have not mapped is still counted, under a guessed name marked with ?, because losing the work is worse than guessing at it. It is marked precisely so it does not go on an invoice as though it were certain.

clientLimits caps a client for the month. A project that has eaten its budget stops on its own, and the other four carry on. Every receipt carries the client, and it is the first column of the CSV export, so the invoice is a filter rather than a reconstruction.

How it works

coding agent ─hook─┐
                    ├──► GVNR ──────► allow / deny / escalate ──► hash-chained receipt
other agents ─proxy─┘        │
                             └──► dashboard (live gauges + decision tape)

Everything is set from the owner console on the dashboard: one panel with a fader for each limit and a switch for each check, so there is one place to answer what these agents may do and what they may spend.

  • Hook: reads the session transcript, totals the tokens, asks GVNR, and translates the verdict into the agent's own allow / deny / ask. If GVNR is down it fails open, so it never blocks your real work.
  • Proxy: a passthrough for /v1/messages and /v1/chat/completions that reads exact usage from responses and refuses when an agent is over its limit. This is the tamper-resistant path, since it runs server-side.
  • Receipts: appended to ~/.enforcer-governor/receipts.jsonl, each line carrying its own hash, folded in from the previous line. GET /verify walks the file and names the first line that does not add up, so an edit or a deletion anywhere in the history is caught, including in a stretch written before the last restart. Receipts written by versions before 0.11 have no stored hash and are reported as unverifiable rather than quietly passed.

Honest limits

  • The proxy buffers responses; streaming passthrough is next.
  • Token totals come from the transcript, which writes asynchronously, so a decision can lag real spend by one turn. Enforcement at the tool boundary makes this safe in practice.
  • On subscription billing, dollar figures are estimates at list prices, labelled est.
  • GVNR answers only this machine: loopback connections, local Host headers, and no cross-origin requests. Controls that loosen a limit (settings, approve, resume, remove) need a key that changes on every start and lives only in the dashboard page. That stops a stray curl or another website; an agent with a shell running as you could still load the dashboard and read the key. Against a deliberate adversary rather than an accident, run the agent in a container or VM and keep GVNR on the host.
  • If GVNR is not running, the hook lets actions through and says so, rather than breaking your agent.

Privacy: anonymous usage counts (off by default)

GVNR sends nothing unless you switch on anonymous usage counts, in the dashboard or with npx enforcer-governor telemetry on. When on, it sends at most one small message a day to gvnr.io/api/t: a random install ID made on your machine, the GVNR version, your operating system, and whether it checked any agent actions that day. Never your code, commands, file paths, agent names, prompts or spend. Receipts never leave your machine.

  • Automated machines (CI, GitHub Actions and similar) and DO_NOT_TRACK=1 never send, whatever the setting. GVNR_TELEMETRY=0 does the same.
  • After two weeks the dashboard may ask one question: how you would feel if you could no longer use GVNR. Your answer is sent only if you press Send.
  • npx enforcer-governor telemetry shows the current setting; telemetry off switches it off.
  • The code is src/telemetry.mjs, and test/telemetry.test.mjs checks the exact fields sent.

Run the tests

npm test

The bigger picture: Enforcer

GVNR is one idea applied to one resource. The idea is Enforcer, Instruxi's policy engine, and it asks a single question in front of every system it guards:

May this identity do this, right now?

Answered three ways (allow, deny, escalate to a human), with a tamper-evident receipt for every answer. Here that identity is an AI agent and the resource is your money. In the full Enforcer platform the same verbs govern who reads a record, who moves funds, who issues a credential, and who approved the exception, across people, services, and agents, for teams that have to prove it to an auditor afterwards.

So this repo is also a working argument: if three verbs and a receipt chain can tame runaway agents on your laptop, the same primitive scales to the systems behind them. That is what we build. instruxi.io

License

Functional Source License 1.1 (FSL-1.1-ALv2). Free to run on your own agents, in production, including commercially. You may not offer it as a competing commercial product or service. Becomes Apache 2.0 two years after each release. See LICENSE. Built by Instruxi.

Keywords

ai-agents

FAQs

Package last updated on 24 Sep 2026

Related posts