Veto/docs
Meow Gateway

Policy Pack

YAML rules compiled into a deterministic AST. Bound into every capsule via policy_sha256 so the runtime rule set is auditable.

A Policy Pack is a YAML document that declares budgets, rails, and rules. The gateway compiles YAML → AST on seed, computes a canonical SHA-256, and every capsule minted under that pack binds policy_sha256 into the signed payload. Auditors can then match a capsule to the exact rule set that authorized it.

Three bundled packs

Pack IDCategoryUse case
ap_strict_v1APAccounts payable — HITL for first-time payees, $5k wires, any international
crypto_fund_v1CryptoStablecoin payouts — per-chain caps, dual-control at $10k, no ACH
fund_admin_v1Fund adminInter-entity book transfers — daily cap, no external rails

All three ship inside the Docker image under /etc/veto/policy-packs/ and seed into storage on first boot.

Shape

id: ap_strict_v1
version: 1
category: ap
title: Accounts Payable — Strict

defaults:
  quarantine_window_hours: 48
  receipt_retention_days: 3650
  dual_control_threshold_usd: 10000

budgets:
  entity_24h_usd: 50000
  entity_7d_usd: 250000
  entity_30d_usd: 1000000
  session_usd: 25000
  counterparty_24h_usd: 15000

rails:
  allowed: [ach, wire, book]
  denied: [international_wire, usdc.eth, usdc.sol]

rules:
  - id: require_invoice_for_pay
    when: { tool: meow.pay }
    require:
      invoice_hash: present
    else: deny
    reason_code: invoice_hash_missing

  - id: require_verified_beneficiary
    when:
      tool: meow.pay
      counterparty.verified_by_human: false
    action: require_approval
    reason_code: first_time_payee

Rule semantics

  • when — predicate; if all conditions match, the rule fires
  • require — positive constraints (present, equality, range); any failure triggers else
  • actionallow / deny / require_approval; else is the rule's fallback
  • reason_code — stamps into both the error envelope AND the decision receipt

Rules evaluate in order. First matching deny or require_approval short-circuits.

Forking

# Copy a bundled pack to start:
cp apps/meow-gateway/src/policy-packs/ap_strict_v1.yaml my-pack.yaml

# Edit, then apply:
curl -X POST http://localhost:3005/v1/policies \
  -H "content-type: application/yaml" \
  --data-binary @my-pack.yaml

Once applied, new mints against your pack id use your rules. The pack's sha256 binds into every capsule so there's no confusion about which revision was enforced.

Rule of thumb: bump the version field whenever you change behavior. The compiled sha256 is already content-addressed, but a version bump surfaces the diff in dashboards and keeps audit trails readable.

How policies gate the mint

  1. Request arrives at POST /v1/capsules
  2. Gateway loads the active pack for the org
  3. evaluatePolicy(pack, context) returns allow | deny | require_approval
  4. On require_approval, the gateway raises a HITL approval row, returns 409 with approval_id
  5. On deny, the gateway returns 403 with the triggering rule's reason_code
  6. On allow, the gateway reserves a budget lease, signs, and chains an allow receipt

At consume time, rules re-evaluate against the runtime request — a capsule minted under one pack cannot be consumed if the pack's sha256 has rotated underneath it.