Skip to main content
Veto rules are YAML files that define guardrails for AI agent tool calls. Rules can be deterministic (static conditions evaluated locally) or semantic (LLM-based validation for natural language policies).

Rule Structure

Every rule has these core fields:
rules:
  - id: unique-rule-id          # required: unique identifier
    name: Human readable name   # required: display name
    enabled: true               # optional: default true
    severity: high              # critical | high | medium | low | info
    action: block               # block | warn | log | allow | require_approval
    tools:                      # optional: empty = applies to all tools
      - tool_name
    conditions:                 # optional: deterministic checks (AND logic)
      - field: arguments.amount
        operator: greater_than
        value: 1000
    description: "Optional semantic guidance for LLM validation"

Actions

Veto supports five action types:
Deny execution immediately. Tool call fails with a ToolCallDeniedError.
action: block
Use for hard security boundaries (e.g., block production deployments, prevent rm -rf).
Route to human approval queue. Execution pauses until approved/denied via callback URL.
action: require_approval
Configure approval webhook in veto.config.yaml:
veto/veto.config.yaml
approval:
  callbackUrl: "http://localhost:8787/approvals"
  timeout: 30000
  timeoutBehavior: "block"
See Approval Workflows for full guide.
Log warning but allow execution. Useful for soft policy enforcement during testing.
action: warn
Log decision only. No blocking, no warning. Ideal for audit trails and monitoring.
action: log
Explicitly permit. Useful for allowlists in combination with global block rules.
action: allow

Tool Scoping

Rules apply to specific tools or all tools:
# Apply to specific tools
tools: [transfer_funds, make_payment]

# Apply to all tools (global rule)
tools: []
Example: Global rule with tool-specific override
rules:
  # Global: require approval for everything
  - id: global-approval
    name: Default approval gate
    action: require_approval
    tools: []  # applies to all tools

  # Override: allow read-only tools
  - id: allow-reads
    name: Allow safe reads
    action: allow
    tools: [get_balance, read_file, list_directory]

Conditions (Deterministic Rules)

Conditions run locally with zero latency. No LLM call. Use dot notation for nested fields.

Field Paths

field: arguments.amount        # tool arguments
field: arguments.user.role     # nested objects
field: context.time            # built-in context (time, day_of_week)

All Operators

# Exact match
- field: arguments.status
  operator: equals
  value: "approved"

# Not equal
- field: arguments.env
  operator: not_equals
  value: "production"

Condition Logic

AND logic (all conditions must match):
conditions:
  - field: arguments.amount
    operator: greater_than
    value: 1000
  - field: arguments.to_account
    operator: starts_with
    value: "EXT-"
OR logic (any group matches):
condition_groups:
  - # Group 1: high amount
    - field: arguments.amount
      operator: greater_than
      value: 10000
  - # Group 2: external recipient
    - field: arguments.to_account
      operator: starts_with
      value: "EXT-"

Semantic Rules (LLM Validation)

For policies that can’t be expressed with static conditions, use semantic guidance:
- id: verify-recipient
  name: Verify payment recipient
  action: block
  tools: [transfer_funds]
  description: |
    Block transfers to recipients that are not verified vendors in the system.
    Check if the recipient is on the approved vendor list.
When conditions are absent or don’t match, Veto sends the rule’s name and description to the LLM for semantic validation.

Hybrid Rules

Combine deterministic + semantic:
- id: high-value-verification
  name: High-value transfer verification
  action: require_approval
  tools: [transfer_funds]
  conditions:
    - field: arguments.amount
      operator: greater_than
      value: 10000
  description: |
    Require approval for large transfers. Ensure the recipient
    is a known business partner and the reason is documented.
Execution flow:
  1. Deterministic conditions checked first (fast, local)
  2. If conditions match, rule triggers immediately
  3. If conditions don’t match, LLM evaluates semantic description

Best Practices

Deterministic First

Use static conditions whenever possible. Zero latency, no LLM cost.
# Good: deterministic
- field: arguments.amount
  operator: greater_than
  value: 1000

# Avoid if deterministic works
description: "Block amounts over $1,000"

Specific Tool Scoping

Narrow rule scope to relevant tools. Reduces false positives.
# Specific
tools: [git_push, deploy]

# Too broad
tools: []  # applies to all tools

Explicit Severity

Tag rules with severity for dashboards and alerting.
severity: critical  # production impact
severity: high      # data/security risk
severity: medium    # policy violation
severity: low       # audit flag

Descriptive IDs

Use kebab-case IDs that describe intent.
# Good
id: block-external-transfers
id: require-approval-for-prod-deploy

# Avoid
id: rule1
id: temp-rule

Expression Syntax (Advanced)

Veto supports compiled policy expressions for complex logic:
conditions:
  - expression: "arguments.amount > 10000 and arguments.to_account starts_with 'EXT-'"
Expressions support:
  • Boolean operators: and, or, not
  • Comparisons: >, <, >=, <=, ==, !=
  • String operators: starts_with, ends_with, contains
  • Grouping: (...)
Note: Expressions take precedence over field/operator/value when both are present.

Real-World Examples

version: "1.0"
name: financial-policies
rules:
  - id: block-large-transfers
    name: Block Large Transfers
    description: Transfers over $10,000 require manual approval
    enabled: true
    severity: high
    action: block
    tools: [transfer_funds]
    conditions:
      - field: arguments.amount
        operator: greater_than
        value: 10000

  - id: block-external-transfers
    name: Block External Transfers
    description: Block transfers to external banks
    enabled: true
    severity: critical
    action: block
    tools: [transfer_funds]
    conditions:
      - field: arguments.to_account
        operator: starts_with
        value: "EXT-"

Testing Rules

Before deploying, test rules with veto guard check:
veto guard check --tool transfer_funds --args '{"amount": 15000}' --json
# {"decision":"block","rule":"block-large-transfers","reason":"amount exceeds $10,000"}
See Testing Policies for comprehensive testing workflows.

Next Steps

Approval Workflows

Set up human-in-the-loop with approval callbacks

Audit Trail

Export decisions as JSON/CSV for compliance

Testing Policies

Write tests and validate rules before deployment

CI/CD Integration

Enforce policy coverage in your build pipeline

Build docs developers (and LLMs) love