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 ID | Category | Use case |
|---|---|---|
ap_strict_v1 | AP | Accounts payable — HITL for first-time payees, $5k wires, any international |
crypto_fund_v1 | Crypto | Stablecoin payouts — per-chain caps, dual-control at $10k, no ACH |
fund_admin_v1 | Fund admin | Inter-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_payeeRule semantics
when— predicate; if all conditions match, the rule firesrequire— positive constraints (present, equality, range); any failure triggerselseaction—allow/deny/require_approval;elseis the rule's fallbackreason_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.yamlOnce 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
- Request arrives at
POST /v1/capsules - Gateway loads the active pack for the org
evaluatePolicy(pack, context)returnsallow | deny | require_approval- On
require_approval, the gateway raises a HITL approval row, returns 409 withapproval_id - On
deny, the gateway returns 403 with the triggering rule'sreason_code - 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.