paybondpaybond
Sign in

T02 · Tutorial

Policy and limits

default_deny policy in action: allow a registered tool, deny unregistered spend, keep allowed_tools aligned with the registry.

~20 minIntroSandboxcommerceoperatorAllow / deny compare

Outcome: Predict allow vs spend_denied from paybond.policy.yaml alone.

Learning view: objectives and extras expanded. Switch for commands only.

Mental model

Policy is the source of truth. default_deny → only registered, evidence-capable tools pass.

You will be able to

  • Point at registry fields that make a tool allowable
  • Predict spend_denied when tools or intent.allowed_tools misalign
  • Validate a policy with paybond policy validate-tools

Prerequisites

  • · First guarded spend complete (or comfort editing paybond.policy.yaml)
  • · A project with a local policy file you can edit

What you'll compare

paybond.policy.yaml is the source of truth for tools, spend ceilings, and evidence. With default_deny: true, anything not registered as side-effecting with an evidence preset fails closed at the tool boundary — before money moves.

Allow — registered tool

Allow

Tool is under tools, side_effecting with an evidence_preset, and listed on intent.allowed_tools. Middleware authorizes up to the tool max.

paybond.policy.yaml

Example

tools:
  travel.book_hotel:
    side_effecting: true
    max_spend_cents: 20000
    evidence_preset: cost_and_completion

intent:
  allowed_tools:
    - travel.book_hotel
  budget:
    currency: usd
    max_spend_usd: 200

Deny — missing or misaligned

Deny

Unregistered tool names, missing evidence presets, or intent.allowed_tools that don’t match the registry → spend_denied (or fail closed at authorize). Trace shows authorize in amber.

Example response

Example

{
  "authorized": false,
  "reason": "spend_denied",
  "operation": "travel.book_flight",
  "detail": "tool not in policy registry / allowed_tools"
}

Try it in your project

Validate then smoke

  1. Step 1 of 4

    Scaffold a policy preset

    Travel preset is the fastest surface for allow vs deny.

    Goal: Start from a known-good registry so diffs are intentional.

    Run this

    paybond policy init --preset travel --out paybond.policy.yaml

    Example response

    Wrote paybond.policy.yaml (travel preset)
    tools: travel.book_hotel, travel.book_flight, …

    You should see: paybond.policy.yaml lists travel.book_hotel under tools.

  2. Step 2 of 4

    Validate tools against policy

    CI-friendly check that registry and intent stay aligned.

    Goal: Catch mismatches before a full smoke.

    Run this

    paybond policy validate-tools --policy ./paybond.policy.yaml

    Example response

    ✓ tools registry ok
    ✓ intent.allowed_tools aligned
    exit 0

    You should see: Exit 0 with no registry/intent errors.

  3. Step 3 of 4 · Allow

    Smoke the allowed operation

    Authorized path with a cost_and_completion evidence body.

    Goal: Registered tool + valid evidence → settles under the ceiling.

    Run this

    paybond agent sandbox smoke --operation travel.book_hotel --requested-spend-cents 20000 --evidence-preset cost_and_completion --result-body '{"status":"ok","cost_cents":18700}' --format json

    Example response

    {
      "authorized": true,
      "operation": "travel.book_hotel",
      "settled_cost_cents": 18700,
      "status": "released"
    }

    You should see: JSON shows authorization success (not spend_denied).

  4. Step 4 of 4 · Deny

    Smoke an unregistered operation

    Deliberate fail-closed path: operation not on the tools map / intent.allowed_tools.

    Goal: With default_deny, unregistered tools deny at authorize — they never silently spend.

    Run this

    paybond agent sandbox smoke --operation travel.book_flight --requested-spend-cents 15000 --evidence-preset cost_and_completion --result-body '{"status":"ok","cost_cents":15000}' --format json

    Example response (denied)

    {
      "authorized": false,
      "reason": "spend_denied",
      "operation": "travel.book_flight"
    }

    Success looks like: JSON shows spend_denied (or fail-closed at authorize) for travel.book_flight when only travel.book_hotel is registered.

    Note: If you used --policy-file on validate, pass the same file on smoke so the registry matches.

Operator checklist

  • Keep default_deny true for multi-tool agents on production attach.
  • Align intent.allowed_tools with side-effecting registry entries only.
  • Use max_spend_cents for fixed ceilings; spend_from_args only for server-resolved amounts — never free-form LLM catalog prices.
  • Deep reference: agent policy docs and remote validate for CI.

Full schema: Agent policy-as-code.

Verify before you continue

Check these off against your terminal or timeline output — progress stays on this device.

0/4

If something goes wrong

  • If you see

    Tool in allowed_tools but still denied

    Do this

    Register it under tools with side_effecting and evidence_preset, not only on the intent list.

  • If you see

    Validation fails for a paid tool

    Do this

    Side-effecting tools need evidence_preset (e.g. cost_and_completion).

  • If you see

    Catalog price comes from the LLM

    Do this

    Use server-resolved amounts / spend_from_args; never free-form model prices for checkouts.

Self-check

Answer without scrolling up — then reveal the model answer to compare.

Why is default-deny safer for multi-tool agents than allow-by-default?

Next steps

Pick a branch — not every path needs every tutorial.

Recipes are copy-paste production smokes — not repeated inside this tutorial.