One set of rules for every AI coding agent your team runs.
Before an agent runs a shell command or writes a file, Paveo checks it. The dangerous ones are refused, and every decision goes into a record your security team can verify. On each engineer's machine, with nothing sent anywhere.
Start by checking the guards you already have: run in a project with Claude Code hooks, uvx paveo doctor names the ones that can never block. Open source under Apache-2.0, free for one developer; $15 per developer a month across a company.
Also guards agents you build in Python, and any MCP server a client starts over stdio.
CHECKPOINT
.paveo/audit.jsonlchain verified
0001allowclaude-code · Bash45d46e01cb
0002denyBash.command.not_matches3ea0ebec19
0003allowcodex · Bashb762a6c19c
0004allowcursor · Writec062a4c283
0005denyShell.command.not_matches2e13e01f7d
Calls reaching the checkpoint, judged by rules from Paveo's starter policies. Each log line's hash covers the line and the one before it, computed in your browser the way Paveo computes it.
Under the hood
Your agent does the work. Paveo decides what it may do.
Every call passes the same six steps, in this order, before it leaves your process.
1
The call arrives
A tool call, a shell command or a model request, from your code or from a coding agent's hook.
2
Who is asking
The agent and the person it acts for. An agent the policy doesn't name is refused, and so is everything after paveo stop.
3
The rules
Which tools, which arguments, in what order, how often. An agent past its plan's limit is refused here too.
4
The ceiling
For a model call, its worst-case cost is held against the agent's budget before anything is sent.
5
The decision
Admitted, or refused with the rule and the reason. In shadow mode, a refusal is recorded and the call goes ahead.
6
The record
Your proof: written to the hash-chained log before the call leaves. When the model answers, its real cost settles the hold.
Every call gets a rule, a ceiling, a decision and a record, before it happens.
For engineering teams
Every engineer's coding agent
One set of rules for every engineer's Claude Code, Codex and Cursor
No code to write. Before the agent runs a shell command or writes a file, the hook checks it, and a refusal goes back to the agent, which tries something else.
the agent calls Bash rm -rf ~/projects
it gets backRefused by policy: Bash.command.not_matches
(constraint_violated). An argument is outside
what this rule permits. Correct it only if it
was a mistake; do not split the action into
smaller calls to fit. If the task cannot be done
within the policy, stop and tell the user; do not
try to reach the same result another way.
Refuses rm -rf, git push --force, git reset --hard, DROP TABLE and edits to its own policy.
paveo stop refuses every call until paveo resume.
paveo replay claude-code shows what it would have refused in sessions you already had.
Also: agents you build
The same checkpoint around every call your own agent makes
Write what each agent may do in a policy file, then route its calls through check_tool and check_llm. Model calls are priced before they are sent, for Anthropic, OpenAI, Gemini and any model you price yourself.
with pf.session(agent_id="refund-bot", principal="user_123") as s:
s.check_tool("refund", {"order_id": "A-1", "amount_usd": 600})
PolicyDenied: refund.amount_usd.max (constraint_violated)
Any MCP client, any language
Put Paveo in front of any MCP server, with no code
Claude Desktop, Cursor, Windsurf: any client that starts a local MCP server over stdio. paveo mcp starts the server for it and checks every tool call against your policy first. A refused call never reaches the server; the client gets the refusal, naming the rule, and the model reads it.
the model calls write_file pwned.txt
it gets backRefused by policy: <undeclared> (tool_not_allowed).the server never sees it, and pwned.txt is never written
The server receives exactly what was judged: every message is passed on as Paveo re-read it, so no difference in how two programs split or parse a message can turn it into a call nobody judged. Tool calls only; resources, prompts and sampling pass through. Local servers over stdio, not remote ones.
Proof before you switch it on
Two months of our own Claude Code, put through the starter policy
We ran paveo replay over our own sessions while building Paveo: 106 sessions and 8,176 tool calls, from 31 July to 27 September 2026. This is what the starter policy would have refused.
117
rm -rf
3
git clean -f
2
git reset --hard
1
git push --force
Plus 547 attempts to edit the agent's own settings or the guard's files. Most of those came from building Paveo itself, whose files the starter policy protects. Not every rm -rf was a mistake: some were clean-ups the agent meant, and the guard would have made it find another way.
$ paveo replay claude-code ~/.claude/projects
paveo: read 106 session files.
Tool calls: 8,176
judged by the policy: 6,918, allowed 6,248, refused 670
...
Nothing was stored, and no prompt, command or argument is shown.
Run it on your own history before you trust the policy: it reads the sessions Claude Code already keeps, stores nothing, and prints only counts and rule names.
What a policy can say
Which tools, with which arguments
An argument that isn't named is refused, and a limit can't be dodged by leaving the argument out.
"amount_usd": {"max": "500.00"}
In what order
A refund only after the same order was looked up, earlier in the same session.
Twenty calls a minute, or the same call twice in thirty seconds: the loop that retries one failing request forever.
"rate": {"calls": 20, "seconds": 60}
Which models, and how much
A daily ceiling in dollars, held against the worst case of each call before it is sent.
"budget": {"period": "day", "limit_usd": "5.00"}
Try it first
Shadow mode records what a rule would refuse and lets the call through, so you can read a day of your agent's work before anything is enforced.
"mode": "shadow"
An agent with no rule for a tool may not call it. Allow-by-default is how agents end up acting outside their job.
Every refusal names its reason
These are the nineteen reasons Paveo can write into the log. Each refusal names one, with the rule that fired, so a year later you can still say why a call was stopped.
Tools and arguments
tool_not_allowed
The agent has no rule for this tool.
tool_denied
The tool is on the agent's deny list.
argument_not_permitted
An argument the rule doesn't name.
argument_missing
A constrained argument was left out.
constraint_violated
An argument is past its limit, or matches a forbidden pattern.
Order and pace
requires_unmet
The call that must come first didn't, with the same values.
rate_limited
More calls than the rule allows in its window.
repeated
The same call again, inside its window.
not_comparable
Arguments too complex to compare with earlier calls.
Models and money
model_not_allowed
A model this agent may not use.
model_denied
A model on the agent's deny list.
no_budget
A model call from an agent with no budget.
budget_exceeded
Its worst case would breach the ceiling.
pricing_unknown
A model or token type the price table can't price.
invalid_request
A request whose cost can't be bounded, such as one with no output cap.
The checkpoint itself
agent_unknown
An agent the policy doesn't name.
stopped
paveo stop is in force.
plan_limit
An agent past the number the plan covers.
memory_unavailable
The guard can't read what ran before, and a rule needs it.
What's inside
Five parts, all in your process
pip install paveo
policy.json
The policy. Agents, tools and their arguments, order, rates, models, budgets and shadow mode. Its hash goes into every record.
check_tool
The checkpoint.check_tool, check_llm and wrap_anthropic in your code, and the hook for Claude Code, Codex and Cursor.
reserve
The ledger. The worst case is reserved, the real cost settled. One lock serves threads and asyncio, so calls at the same moment can't overspend a ceiling.
prices
The price table. Dated prices for Anthropic, OpenAI and Gemini. A model it can't price is refused, never guessed.
audit.jsonl
The record. Hash-chained, with an anchor at its end. Exported as audit evidence on Team and up.
Proof after
A record you can hand to an auditor
Every decision goes into a hash-chained log on your disk: which agent, which tool or model, allowed or refused, by which rule, under which policy. Editing any record breaks every hash after it.
On Team and up, one command turns a period of it into evidence: a report to read or print, every record as a spreadsheet, the records themselves so the chain can be recomputed, and the policy that was in force. It refuses a log whose chain doesn't verify.
$ paveo evidence --since 2026-09-01 --until 2026-09-30
paveo: 420 records (seq 1-420) written to
paveo-evidence-2026-09. The chain verified from
record 1 to 420, and matches its anchor.
The report that command wrote, unedited. The log behind it is sample data: two agents, 420 decisions in September.
For the security review
You are being asked to put Paveo in the path of every call your agent makes. These are the answers a reviewer asks for, each one something you can check.
Network
Paveo opens no sockets. A test fails the build if one is created, and you can confirm it on your own machine with a firewall rule.
What is recorded
Agent, principal, tool or model name, decision, rule, policy hash, costs and token counts.
What is never recorded
Prompts, completions and tool arguments. A tool name the policy doesn't define is logged as <undeclared>, so a model can't write into the log through it.
Dependencies
None. Paid plans add one optional package, cryptography, used only to check a licence key's signature, offline.
Failure
A call Paveo can't judge is refused: a policy that won't load, a call it can't price, a log it can't write. Failing open is a setting that is off unless you switch it on, and it warns on every call while it is on.
The code
Open source under Apache-2.0, except the paid evidence export, which is under the Elastic License 2.0 and never loaded by anything free. Read every line; releases are published only by CI, with a provenance attestation and an SBOM.
Paveo runs entirely on your machines, so there is no service of ours for your agents' data to reach: no status page to watch, no subprocessor handling that data, and no SOC 2 report to request, because there is nothing of ours to audit but the code.
It is a guard rail, not a sandbox. Code that calls your provider directly, without asking Paveo, is never seen.
The log is tamper-evident, not tamper-proof. Someone with write access can rewrite it; they can't do it invisibly.
The coding-agent guard matches patterns. It doesn't understand the shell, so rm -r -f or a script that does the deleting gets past it.
Budgets live in one process. A restart clears the spend record, and two processes don't share a ceiling yet.
It doesn't read model output, filter content or detect jailbreaks.
The MCP guard judges tool calls only. Resources, prompts and sampling pass through, a path in a call is judged as written (symbolic links are followed only for the coding agents), an argument spelled in another case is a different name unless the tool lists its constraints, it guards local servers over stdio, and a client set up to start a server without paveo mcp is not guarded.
Pricing
Agents you build are priced per agent; coding agents across a company, per developer. No per-request fees: checks run on your machine, so there is no limit on them and nothing for us to count.
Developer
For one person, with every coding agent they run.
$0 forever
Up to 3 agents per policy
Unlimited checks, on your machine
Every rule: which tools, which arguments, in what order, how often
Shadow and enforce modes
Model-call budgets, held at each call's worst case
The coding-agent seatbelt for Claude Code, Codex and Cursor
Replay of your past Claude Code sessions through the policy
The hash-chained audit log, on your disk
pip install paveo
Across a company
For whoever answers for every engineer's coding agent.
$15 per developer a month
No minimum: from one developer
Unlimited checks, on your machine
Audit evidence from each developer's log
Email support, reply within 2 working days
An hour with us writing the policy you roll out to everyone
One developer guarding their own machine stays on Developer, free
Developer is the free way to try Paveo: every rule, on your machine, for up to three agents. A paid plan is a licence key, checked offline, with no account. When a plan ends, the free plan applies: every rule keeps enforcing, the first three agents keep working, and any past three are refused until the plan is renewed.
Your team
The same rules for every engineer's agent.
For whoever answers for what the engineers' coding agents can do: Claude Code, Codex and Cursor under the same rules, and a record from each machine for your security team.
What we do with you:
Set it up across your team within 48 hours. We write the policy with you, run it in shadow mode first so nothing is blocked by surprise, then switch it on.
Check the guards you already have.paveo doctor on your repositories names the Claude Code hooks that can never block.
Evidence from each machine. On Team and up, paveo evidence turns a period of a developer's log into a report your security team can verify.
The founder, every week. A weekly call, and what you run into gets fixed fast, usually within two days.