Boundaries

Declare what your agent is allowed to do.

A boundary manifest is a typed contract for an agent run: which tools it may call, how many steps, how much it may spend, which data scopes it may touch. The SDK enforces in-process and breaks the moment a boundary is crossed. The server-side boundary_violation policy signs an audit trail of any breach.

What it is

One declarative shape, two enforcement points:

  • Four dimensions. allowed_tools, max_steps, budget_usd_per_run, data_scopes. One shared JSON schema in lib/boundaries/schema.ts.
  • Client-side break. ProvaBoundaries raises BoundaryViolationError the moment any dimension is crossed. The agent runtime stops before the next call.
  • Server-side audit. A built-in policy reads the same manifest from the signed agent_run_manifest receipt and re-checks every ingested event against it, signing any violation, so a breach is caught and recorded even when the SDK guard is off.
  • Contract-tested. Python and TypeScript implementations are pinned by a parity test ([tests/sdks/test_boundaries_contract.py](https://github.com/cobound/prova)) that round-trips fixtures through both and asserts identical violation dicts.
  • Live dashboard. /dashboard/boundaries shows every run with active bounds, steps used / max, cost used / budget, and any current breach.

What "prove it didn't exceed" means precisely. For decisions recorded as receipts, Prova attests: a breach is caught and signed, and an auditor can verify it offline, but the proof is only as complete as your instrumentation. For traffic routed through the gateway, Prova guarantees: a disallowed call is blocked before it runs. In guarantee mode the gate is fail-closed, so even a Prova outage cannot let a call through unscreened. Each gateway-routed call carries a signed marker proving it passed the gate, so "what share of my traffic is guaranteed" is a measured number, not a promise. The dashboard shows your gateway coverage next to your active runs.

What it does for you

The runtime your agent can't accidentally exceed.

Most multi-agent failures aren't bugs in any single agent. They're emergent: the system as a whole takes 200 steps when 20 were expected, or calls a tool nobody approved, or touches a customer record outside the declared scope. Boundaries lock the system's outer shape so emergent behavior gets caught at the edge, not in production.

One manifest, two enforcement points.

The SDK enforces locally for the immediate break. The server policy enforces from the audit trail for the auditable record. The two implementations are pinned to one schema and verified by a contract test, so an auditor verifying a violation receipt sees the same verdict the runtime saw.

Per-run budget, not just per-month.

A monthly_budget_cap protects the org. A budget_usd_per_run protects a single run from spiking. Combined: a runaway loop in one run can't drain the org's monthly budget before anyone notices, because each run dies at its own ceiling first.

The boundary receipt is the audit artifact.

The agent_run_manifest receipt is signed at run-start. Every subsequent event in the run carries the run_id. A regulator asks 'was the AI agent operating within its declared scope?' and the answer is a signed manifest plus a signed event stream that any verifier can replay offline.

How it ships

from prova_cp import ProvaBoundaries, ProvaCallbackHandler, BoundaryViolationError

boundaries = ProvaBoundaries(
    allowed_tools=['search', 'send_email'],
    max_steps=20,
    budget_usd_per_run=0.50,
    data_scopes=['customer_id'],
)
handler = ProvaCallbackHandler(prova, boundaries=boundaries, break_on_violation=True)
try:
    graph.invoke(inputs, config={'callbacks': [handler]})
except BoundaryViolationError as e:
    log.error('boundary breached: %s', e.match['dimension'])

Tamper-evident by default

Every boundary check lands in the Audit Vault as an Ed25519-signed receipt. It is verifiable offline against a published public key, with no Prova account and no Prova in the loop. Change one byte and the signature breaks.

Verify a receipt yourself →