sudo for AI agents.
Stop agents from deleting files, leaking secrets, or pushing to prod — without slowing anyone down.
docs.veto.so · veto.so · npm · PyPI
AI agents can execute code, call APIs, and modify production systems. Veto is the permission layer that sits between every agent action and execution — validating, blocking, or routing to human approval before anything runs.
const veto = await Veto.init(); // loads ./veto/veto.config.yaml + rules
const guarded = veto.wrap(tools); // inject guardrails — types preserved
// pass guarded to your agent. done.The agent is unaware it's being governed. Your tools are unchanged. No behavior change for the AI.
┌───────────┐ ┌────────────┐ ┌──────────────┐
│ AI Agent │────────▶│ Veto │────────▶│ Your Tools │
│ (LLM) │ │ (Guard) │ │ (Handlers) │
└───────────┘ └────────────┘ └──────────────┘
│
┌─────┴──────┐
│ YAML Rules │ block · allow · ask
└────────────┘
- Agent calls a tool
- Veto intercepts and validates (deterministic conditions first, optional LLM for semantic rules)
- allow → executes normally · block → denied with reason · ask → human approval queue
| Package | Language | Install | Description |
|---|---|---|---|
veto-sdk |
TypeScript | npm install veto-sdk |
SDK for guarded agentic apps |
veto |
Python | pip install veto |
Same API, all major LLM providers |
veto-cli |
TypeScript | npm install -g veto-cli |
Interactive studio + headless automation |
veto-bash |
Rust + Node | npm install -g veto-bash |
Rust-first guarded bash runtime + MCP |
npm install veto-sdk
npx veto init # creates ./veto/veto.config.yaml + default rulesimport { Veto } from "veto-sdk";
const veto = await Veto.init();
const guarded = veto.wrap(myTools); // LangChain, Vercel AI SDK, or any custom toolspip install veto
veto initfrom veto import Veto
veto = await Veto.init()
guarded = veto.wrap(my_tools)Rules are YAML files in ./veto/rules/. Static conditions run locally with no API call. LLM validation is opt-in for semantic rules.
rules:
- id: block-large-transfers
name: Block transfers over $1,000
action: block
tools: [transfer_funds]
conditions:
- field: arguments.amount
operator: greater_than
value: 1000
- id: require-approval-for-push
name: Require human approval before pushing to main
action: ask
tools: [git_push]
description: "Intercept any push targeting the main branch."Actions: block · allow · warn · log · ask (human-in-the-loop)
→ Full TypeScript SDK docs · Python SDK docs
For agents that spend money — trading bots, paid API calls, SaaS billing — Veto adds cost-aware policy enforcement.
# veto.config.yaml
extends: "@veto/economic-agent"const result = await veto.guard(
"purchase",
{ item: "GPU" },
{
economic: {
cost: 42.5,
currency: "USD",
payer: "team-wallet",
protocol: "custom",
},
}
);
// result.decision: 'allow' | 'deny' | 'require_approval'
// (YAML uses action: block/ask — guard() returns the resolved decision)Built-in protocol connectors for x402 (HTTP 402), Stripe MPP, and Google AP2. Session budgets enforced locally; agent/user/global budgets via Veto Cloud.
Economic Authorization Guide →
npx veto-cli@latest # launch interactive Veto Studio (TUI)
npx veto-cli@latest policy generate \
--tool transfer_funds \
--prompt "block over $500 to unverified recipients"
npx veto-cli@latest guard check \
--tool transfer_funds --args '{"amount": 600}' --json
npx veto-cli@latest scan --fail-uncovered # CI gate: exit 1 on unguarded toolsnpm install -g veto-bash
mkdir -p "$HOME/.veto/bin"
VETO_BASH_NATIVE="$(veto-bash native-path)"
ln -sf "$VETO_BASH_NATIVE" "$HOME/.veto/bin/bash"
export PATH="$HOME/.veto/bin:$PATH"
export VETO_BASH_REAL_BASH=/bin/bash
VETO_API_KEY=veto_... bash -c 'echo hello'
veto-bash mcp init
veto-bash mcp serve --veto-api-key "$VETO_API_KEY"veto-bash is now Rust-first: the native runtime handles bash interception, deterministic local evaluation, SWR cloud policy refresh, approval polling, audit spooling, and MCP stdio serving. Use veto-bash native-path once during setup, then shadow bash with that packaged native binary so warm executions stay fully native. Cold or unsupported cases fall back to cloud POST /v1/validate. Interactive shells still pass through to the real system bash.
This PR does not add a full cross-platform prebuilt binary pipeline yet. The current package/build flow is honest about that: build on the target platform or install an artifact produced for the same platform/arch.
- Deterministic-first — Static conditions run locally, zero latency, no API call. LLM validation only when you need semantic reasoning.
- Provider agnostic — Works with OpenAI, Anthropic, Google, LangChain, Vercel AI SDK, and any custom tool-calling setup.
- Human-in-the-loop —
askaction routes sensitive decisions to an approval queue instead of auto-blocking. - Audit trail — Every decision logged with tool name, arguments, rule matched, and outcome. Exportable as JSON or CSV.
- Local-first — No cloud required. Fully offline. Optional Veto Cloud for team sync and dashboard.
- Rate limiting — Per-rule sliding window rate limits. In-memory or Redis-backed for distributed deployments.
- Zero-config defaults —
veto initgenerates sensible baseline rules. Production-hardened in under 10 minutes.
Attach sliding window rate limits to any rule. Scopes: global, agent, user, session.
rules:
- id: rate-limit-api
name: Limit API calls
action: block
tools: [call_api]
rate_limits:
- scope: user
max_calls: 10
window_seconds: 60The default store is in-memory. For distributed deployments, plug in RedisRateLimitStore:
import { RedisRateLimitStore } from "veto-sdk";
const store = new RedisRateLimitStore(redisClient, "veto:rl:");Every decision is appended to a tamper-evident hash chain. Each record's SHA-256 hash covers the previous hash plus the record's deterministic JSON serialization. Mutating any historical record invalidates all subsequent hashes.
import { computeChainHash, GENESIS_HASH } from "veto-sdk";
let prevHash = GENESIS_HASH;
const hash = computeChainHash(prevHash, {
tool: "transfer_funds",
decision: "allow",
});Verify integrity from the CLI:
npx veto-cli audit verifyYAML fixture files validated against your policy with deterministic replay -- no LLM, no network.
# ./veto/tests/transfers.yaml
suite: Transfer rules
tests:
- id: block-large-transfer
tool: transfer_funds
arguments: { amount: 5000 }
expect:
decision: block
rule_id: block-large-transfersnpx veto-cli test # run all fixtures
npx veto-cli test --coverage # show untested rule IDsIntercept OpenAI and Anthropic streaming responses, validate tool calls against policies before forwarding to the client.
npx veto-cli intercept --port 8080 --target https://api.openai.comPoint your application at http://localhost:8080 instead of the provider URL. Supports auto-detection of OpenAI and Anthropic SSE formats.
Optional @opentelemetry/api peer dependency. When installed, Veto instruments guard checks with spans. When not installed, all tracing functions are no-ops -- zero overhead.
Send decision events to external systems. Built-in formatters for Slack, PagerDuty, generic JSON, and CEF (ArcSight). Configure webhook endpoints per event type: tool_call_blocked, approval_requested, rate_limit_exceeded, and others.
npx skills add PlawIO/vetoInstalls veto-policy-runtime — gives Claude Code, Cursor, and Windsurf safe, non-destructive policy operations without any SDK integration. Ideal for teams that want guardrails at the coding-agent level.
Veto also fits agent runtimes outside coding workflows. One practical pattern is a Polymarket MCP server where market reads stay open, mutating tools are policy-gated, and larger actions route to approval before execution.
See the Guarded Polymarket MCP guide for the architecture and YAML rules, or the full reference implementation in PlawIO/polymarket-cli-veto.
The OSS SDK runs entirely local. Veto Cloud adds:
- Natural language → policy YAML (no manual YAML writing)
- Central policy sync across all team repos
- Dashboard: decisions, blocked calls, pending approvals
- Approval workflows for human-in-the-loop at scale
- Rate limiting (in-memory + Redis)
- Tamper-evident audit chain
- OpenTelemetry tracing integration
- SSO, audit export, compliance reporting
See CONTRIBUTING.md. On your first PR, a bot will ask you to sign the CLA — takes 30 seconds, one comment.
Report vulnerabilities to security@plaw.io. See SECURITY.md for the full disclosure policy.
Apache-2.0 © Plaw, Inc.