Veto is the policy runtime for AI agent tool calls — write deny rules in plain English, enforce them deterministically, in 5 lines of code.
Veto sits between an agent and the tools it can execute. It evaluates tool name + arguments against deterministic policy, then allows, denies, warns, logs, or routes to approval before your handler runs. It governs tool calls, not prompts.
Checked-in PR-mode baselines are measured on GitHub Actions runner output, not local hardware or thresholds. CI gates Veto p99 regressions above 10% against benchmark/baselines/*.json; absolute thresholds remain separate.
| Runtime | Workload | Iterations | p50 | p95 | p99 | p99 threshold | Source |
|---|---|---|---|---|---|---|---|
| Veto | single-rule local eval | 50,000 | 0.000260ms | 0.001092ms | 0.002594ms | 0.05ms | measured on GitHub Actions PR-mode, PR #208 CI log |
| Veto | 100-rule merged packs | 50,000 | 0.027291ms | 0.037470ms | 0.057136ms | 0.5ms | measured on GitHub Actions PR-mode, PR #212 CI log |
| Veto | localhost PDP server eval | 250 | 0.402051ms | 0.841651ms | 2.092263ms | 30ms | local loopback fixture baseline; not run in PR mode |
| AGT | policy eval latency per rule | published | 0.012ms/rule | not published | not published | n/a | source: published, not reproduced |
| AGT | throughput at 50 concurrent agents | published | 35K ops/sec | not published | not published | n/a | source: published, not reproduced |
import { protect } from "veto-sdk";
const safeTools = await protect(tools);npm install veto-sdkPass safeTools to LangChain, Vercel AI SDK, OpenAI Agents, MCP adapters, or your own tool runner. If ./veto/veto.config.yaml and ./veto/rules/*.yaml exist, protect() loads them. Without local policy, Veto applies @veto/safe-defaults in observe mode so suspicious shell/file/db/money-movement patterns are logged without surprise blocking.
For a blocking local policy in under a minute:
npm i veto-sdk openai
npx veto init
node examples/60-second-denied-call/denied-call.mjsnpm install veto-sdkimport { protect } from "veto-sdk";
const safeTools = await protect(tools);
const agent = createAgent({ tools: safeTools });pip install vetofrom veto import protect
safe = await protect(tools)
agent = create_agent(tools=safe)npx veto initnpx veto init creates ./veto/veto.config.yaml and ./veto/rules/defaults.yaml. The default local rules are strict and include deterministic denials for sensitive paths and destructive shell commands.
rules:
- id: block-large-transfers
name: Block transfers over $1,000
enabled: true
severity: high
action: block
tools: [transfer_funds]
conditions:
- field: arguments.amount
operator: greater_than
value: 1000Actions: block, allow, warn, log, require_approval.
Veto.init() and .wrap() remain supported for advanced/internal-facing integrations that need an explicit instance, guard() checks, cloud/self-host options, audit exports, or event hooks.
import { Veto } from "veto-sdk";
const veto = await Veto.init({ configDir: "./veto", mode: "strict" });
const safeTools = veto.wrap(tools);
const decision = await veto.guard("transfer_funds", { amount: 1500 });| Package | Language | Install | Purpose |
|---|---|---|---|
veto-sdk |
TypeScript | npm install veto-sdk |
Policy runtime for agent tool calls |
veto |
Python | pip install veto |
Python parity SDK with protect() |
veto-cli |
TypeScript | npm install -g veto-cli |
Init, test, scan, Studio, and policy operations |
veto-bash |
Rust + Node | npm install -g veto-bash |
Native bash tool-call enforcement path |
create-veto-app |
TypeScript | npm create veto-app |
Starter TypeScript app |
docker compose up
curl -s http://localhost:3001/v1/validate \
-H 'content-type: application/json' \
-d '{"toolName":"bash","arguments":{"command":"echo hello"}}'See docs/self-hosting.md for the public reviewer path and the local unauthenticated validation contract.
Veto BYOC runs in the customer plane. Public install artifacts are in helm/, terraform-modules/, cf-templates/, and cdk/. They are outbound-only and must not grant Plaw cross-account IAM or impersonation. Customer policy, decision rows, tool arguments, agent IDs, user IDs, Slack content, prompts, environment variables, and secrets do not cross to Plaw.
Allowed outbound control-plane checks are limited to license heartbeat and optional telemetry. The heartbeat schema is exactly six fields: instance_uuid, license_id, decision_count_30d, sdk_version, operator_version, timestamp.
Install docs:
- Deterministic local evaluation for policy rules that can be checked from tool arguments.
- Provider-agnostic wrapping for any agent framework or custom tool runner.
- Human approval and cost-aware governance for sensitive tool calls.
- Local-first operation with optional self-hosted or cloud policy distribution.
- Apache-2.0 licensed packages and public supply-chain verification artifacts.
See CONTRIBUTING.md. Report vulnerabilities to security@plaw.io.
Apache-2.0 © Plaw, Inc.