Quick Start
Set up Veto in 5 minutes, or let your AI coding agent do it with one prompt.
There are two ways to get started:
| Path | For whom | Time |
|---|---|---|
| Manual setup | Developers integrating by hand | ~5 minutes |
| AI agent integration | AI coding agents (Claude Code, Cursor, OpenCode, Codex, etc.) | ~1 minute |
Manual setup
Prerequisites
- Node.js 18+ or Python 3.9+
- An existing project with AI tool calls you want to guard
- No API key or account needed — Veto works locally out of the box
1. Install the SDK
npm install veto-sdkpip install veto2. Wrap tools in one line
Use protect() as the default entrypoint:
import { protect } from 'veto-sdk';
const safeTools = await protect(tools);from veto import protect
safe_tools = await protect(tools)For advanced control (custom config paths, explicit cloud/self-hosted settings, lifecycle ownership), use Veto.init() directly.
3. Initialize configuration
npx veto initThis creates a veto/ directory:
veto/
├── veto.config.yaml
└── rules/
└── defaults.yamlThe generated config defaults to local mode — all validation runs in-process using your YAML rules. No API key, no account, no network calls.
4. Write your first rule
Edit veto/rules/defaults.yaml. Here's a practical rule that blocks dangerous file operations and large transfers:
rules:
- id: block-sensitive-paths
name: Block access to sensitive paths
action: block
severity: critical
tools:
- read_file
- write_file
- delete_file
conditions:
- field: arguments.path
operator: matches
value: "(\\.env|/etc/passwd|credentials|secrets)"
- id: limit-transfers
name: Block large transfers
action: block
severity: critical
tools:
- transfer_funds
conditions:
- field: arguments.amount
operator: greater_than
value: 100005. Add to your existing code
Pick your framework — every example below is complete and runnable.
import OpenAI from 'openai';
import { protect } from 'veto-sdk';
const openai = new OpenAI();
const tools = await protect([
{
type: 'function',
function: {
name: 'transfer_funds',
description: 'Transfer money to an account',
parameters: {
type: 'object',
properties: {
amount: { type: 'number' },
to: { type: 'string' },
},
},
},
},
]);
const response = await openai.chat.completions.create({
model: 'gpt-5.2',
tools,
messages: [{ role: 'user', content: 'Transfer $500 to Alice' }],
});import Anthropic from '@anthropic-ai/sdk';
import { protect } from 'veto-sdk';
const anthropic = new Anthropic();
const tools = await protect([
{
name: 'transfer_funds',
description: 'Transfer money to an account',
input_schema: {
type: 'object',
properties: {
amount: { type: 'number' },
to: { type: 'string' },
},
},
},
]);
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
tools,
messages: [{ role: 'user', content: 'Transfer $500 to Alice' }],
});import { Veto } from 'veto-sdk';
import { createVetoMiddleware } from 'veto-sdk/integrations/vercel-ai';
import { generateText, tool, wrapLanguageModel } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
const veto = await Veto.init();
const middleware = createVetoMiddleware(veto);
const model = wrapLanguageModel({
model: openai('gpt-5.2'),
middleware,
});
const result = await generateText({
model,
tools: {
transfer_funds: tool({
description: 'Transfer money to an account',
parameters: z.object({
amount: z.number(),
to: z.string(),
}),
execute: async ({ amount, to }) => {
return { success: true, amount, to };
},
}),
},
prompt: 'Transfer $500 to Alice',
});import { Veto } from 'veto-sdk';
import { createVetoLangChainMiddleware } from 'veto-sdk/integrations/langchain';
import { createAgent } from '@langchain/core/agents';
import { DynamicTool } from '@langchain/core/tools';
const veto = await Veto.init();
const middleware = createVetoLangChainMiddleware(veto);
const tools = [
new DynamicTool({
name: 'transfer_funds',
description: 'Transfer money to an account',
func: async (input) => JSON.stringify({ success: true, ...JSON.parse(input) }),
}),
];
const agent = createAgent({
tools,
middleware: [middleware],
});See LangChain integration for ToolNode and callback patterns.
import asyncio
from openai import OpenAI
from veto import protect
async def main():
client = OpenAI()
tools = await protect([
{
"type": "function",
"function": {
"name": "transfer_funds",
"description": "Transfer money to an account",
"parameters": {
"type": "object",
"properties": {
"amount": {"type": "number"},
"to": {"type": "string"},
},
},
},
},
])
response = client.chat.completions.create(
model="gpt-5.2",
tools=tools,
messages=[{"role": "user", "content": "Transfer $500 to Alice"}],
)
asyncio.run(main())import { Veto } from 'veto-sdk';
const veto = await Veto.init();
// MCP tools use inputSchema — Veto auto-detects the format
const tools = veto.wrap([
{
name: 'read_file',
description: 'Read a file from the filesystem',
inputSchema: {
type: 'object',
properties: {
path: { type: 'string' },
},
required: ['path'],
},
},
]);See MCP integration for full server setup.
6. What a denied call looks like
When the agent tries to violate a rule, Veto throws a ToolCallDeniedError:
import { ToolCallDeniedError } from 'veto-sdk';
try {
// Agent tries transfer_funds({ amount: 50000, to: "offshore-account" })
// This triggers the "limit-transfers" rule
await wrappedTool.handler({ amount: 50000, to: 'offshore-account' });
} catch (error) {
if (error instanceof ToolCallDeniedError) {
error.toolName; // "transfer_funds"
error.reason; // "Amount 50000 exceeds limit of 10000"
error.callId; // "tc_abc123"
error.validationResult; // { decision: "deny", reason: "...", metadata: {...} }
}
}The agent receives the denial as a tool error and can adjust its behavior — feed the error back to the model so it retries with different arguments, throw to the caller, or degrade gracefully. See the error handling guide for framework-specific patterns.
7. Go to production
Everything above works locally — no account needed. When your agent goes to production, you'll want visibility into what it's doing: which tools it calls, which calls get denied, and the ability to require human approval for sensitive operations.
Add an API key and the SDK switches to cloud mode automatically:
const veto = await Veto.init({ apiKey: 'veto_...' });
// Or set VETO_API_KEY environment variable — the SDK auto-detectsGet your API key at runveto.com (free tier available). Your existing YAML rules keep working — cloud mode adds capabilities on top:
| Capability | Local | Cloud |
|---|---|---|
| YAML rule validation (~1-5ms) | Yes | Yes (cached locally) |
| Real-time decision dashboard | — | Yes |
| Human-in-the-loop approvals | — | Yes |
| LLM-based semantic policies | — | Yes |
| Decision analytics and audit log | — | Yes |
Policy packs (@veto/coding-agent, @veto/financial, etc.) | — | Yes |
The local rules you wrote in step 4 stay exactly the same. Cloud mode doesn't replace them — it extends what's possible.
Need to self-host? The server is available at ghcr.io/vulnzap/veto-server:latest — see SDK Modes.
AI agent integration
If you're using an AI coding agent (Claude Code, Cursor, OpenCode, Codex, etc.), paste this prompt to have it integrate Veto into your codebase:
## Task: Integrate Veto SDK into this codebase
Veto is the authorization layer for AI agents — it intercepts every tool call and validates
it against rules before execution. The agent never knows the guardrail exists.
### Instructions
1. Read the full documentation: https://docs.runveto.com/llms.txt
2. Install the SDK:
- TypeScript: `npm install veto-sdk`
- Python: `pip install veto`
3. Initialize: `npx veto init` (creates `veto/` directory with config and rules)
4. Find where tools are defined in this codebase and wrap them with `protect(tools)`
5. Create sensible default rules in `veto/rules/defaults.yaml` based on the tools in use
### Key integration pattern
```typescript
import { protect } from 'veto-sdk';
const wrappedTools = await protect(existingTools); // drop-in replacement
```
### Reference docs
- Quick start: https://docs.runveto.com/docs/getting-started/quick-start
- YAML rule format: https://docs.runveto.com/docs/rules/yaml-format
- Framework-specific guides: https://docs.runveto.com/docs/integrations/openai-sdk
(also: anthropic-sdk, vercel-ai, langchain, langgraph, mcp, browser-use,
openai-agents, crewai, pydanticai, playwright, google-gemini)
### Optional: enable cloud mode
If you have a Veto API key, pass it to init for dashboard + approvals + LLM policies:
```typescript
const veto = await Veto.init({ apiKey: process.env.VETO_API_KEY });
```What the agent will do:
- Read the Veto docs at
docs.runveto.com/llms.txt - Install the SDK in your project
- Run
npx veto initto scaffold the config directory - Find your existing tool definitions and wrap them with
protect() - Generate default rules based on the tools it finds
- Optionally enable cloud mode if you provide an API key
Common rules to start with
Copy these into veto/rules/defaults.yaml as a starting point:
rules:
# Block large financial transfers
- id: limit-transfers
name: Block large transfers
action: block
severity: critical
tools:
- transfer_funds
- send_payment
- create_invoice
conditions:
- field: arguments.amount
operator: greater_than
value: 10000
# Block dangerous URLs in browser tools
- id: block-dangerous-urls
name: Block suspicious URLs
action: block
severity: high
tools:
- navigate
- goto
- open_url
conditions:
- field: arguments.url
operator: matches
value: "(data:|javascript:|file://|localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0)"
# Require approval for destructive actions
- id: approve-destructive
name: Require approval for destructive operations
action: require_approval
severity: high
tools:
- delete_file
- drop_table
- remove_user
- delete_repository
# Rate-limit tool calls per session (omit tools to apply to all)
- id: rate-limit-calls
name: Limit total tool calls per session
action: block
severity: medium
conditions:
- field: session.totalCalls
operator: greater_than
value: 100See YAML Rule Format for the full syntax and Policy Packs for pre-built rule sets.
Next steps
| Where to go | What you'll learn |
|---|---|
| YAML Rule Format | Full rule syntax — actions, conditions, condition groups, severity |
| Constraints Reference | Every constraint type — ranges, regex, enums, arrays |
| Error Handling | What to do when tools are denied — retry, degrade, inform |
| Policy Packs | Pre-built rule sets you can extend |
| SDK Modes | Local, cloud, and self-hosted mode explained |
| TypeScript SDK | Full API reference |
| Python SDK | Full API reference |