Skip to main content

runbound

Runtime controls for autonomous AI agents. runbound runs inside your agent's process and stops runaway cost, loops, tool abuse and provider failures deterministically, by counting, timing and hashing, never by asking a model whether something looks wrong. It reads no prompts and no replies, it puts no gateway in your hot path, and it never speaks to your users: when it stops something it hands control back to you and you answer in your own voice.

This is for the platform team that owns AI agents in production, the people who get paged when one runs away, not the people who wrote its prompt.

One agent burned $2,847 in four hours on a refactoring loop while every monitoring dashboard stayed green. Nobody was watching it at 3am, and nothing in the stack had the authority to pull the plug. runbound is the layer with the authority to pull the plug, and the proof that it did — the whole story is in Why this exists.

Install

pip install runbound

There are no runtime dependencies — stdlib only. LangChain support is the one extra: pip install "runbound[langchain]".

Quick start

Guard every model call in the process:

import runbound
runbound.init(budget_usd=5.0, on_anomaly="raise")   # everything below is now guarded

Scope every control to one unit of work:

with runbound.session(f"run:{run_id}"):
    answer = client.chat.completions.create(...)

Refuse an action before its function body runs:

@runbound.tool(max_calls=1)            # the rule lives on the tool, in the same diff
def issue_refund(user: str, amount: float): ...

The full five-minute version — wiring all three sensors, checking what is actually guarded, catching the trip, and the offline demos that need no API key — is in Getting started.

Three levels of protection

You choose how far in to go. Each level is a small, separate step, and most of the value arrives at the first one. The full text is in Three levels of protection.

Level 1: guard the model calls. Two lines. runbound.init() wraps the OpenAI and Anthropic clients for you (or you call runbound.wrap(client) on any client with that shape). From then on every model call is counted, timed and priced, and you get: dollar and token budgets, per-call caps, velocity limits, step and wall-clock limits, the spike ladder, error-storm detection with a circuit per provider, and refusal profiles that carry your own status and sentence. All of that is what the free SDK does on its own, with no network call and no account. Nothing is decorated, no policy is written. See What it controls.

Level 2: one session per run. One line per unit of work. Wrap each run in runbound.session(key) — the key is any id you already have: a run id, a job, a tenant, a customer — and every control above becomes per key instead of per process: this run's budget, this run's ladder, this run stopped, every other caller untouched. See Runs keyed by any id.

Level 3: refuse actions before they run. Only for the tools that matter. Decorate the two or three functions that are irreversible or reach the outside world with @runbound.tool, write a policy (deny this tool, at most once per run, needs approval, or a predicate of your own over the arguments, run in your process), and the call is refused before the function body runs. The rules are yours; runbound enforces and records them, it never judges the action. See Action policy.

Nothing here protects against a bad answer: hallucinations, prompt injection and output quality are out of scope by design. "The agent said something wrong" is a different product. "The agent would not stop" is this one.

What it never sees

  • Prompts and model replies are never read, stored, or sent — except the one narrow case of estimate_tokens=True, which counts characters when the server reported no usage.
  • Tool arguments are sha256-hashed before storage, salted per process. The loop detector compares digests of (tool_name, args, sorted kwargs) mixed with a random salt generated once at import, never the arguments themselves.
  • Raw arguments never leave your process. They are not stored, not logged, and not included in any alert payload.
  • A session key reaches a connected plane as a hash. The plane gets sha256(key) and nothing else, unless you opt in with send_session_keys=True.
  • Failures are recorded as the exception's class name. Only the exception's class name, never its message, is what reaches the plane.
  • No network calls except the ones you configure. No telemetry, no phone-home, no hosted backend you did not point us at. With no token and no control_plane_url set, runbound opens no sockets at all.

The full accounting — every number, where it comes from, and what goes blind without each sensor — is in What the SDK actually sees and Privacy.

Documentation

The manual lives in docs/; it is also published, with executed snippets, at runbound.co/docs.

  • Getting started — install, wire the three sensors around one agent run, and check what is actually guarded.
  • Concepts — the three levels, why this exists, how it works, what it controls, and exactly what the SDK sees and never sees.
  • Guides — keyed runs, fleet mode, spike detection, action policy, self-hosted models, LangChain, async and streaming.
  • Reference — every configuration field, the detectors, what happens when something trips, the circuit breaker, limits, the compatibility matrix, privacy, and the guarantees.

Roadmap is what is shipped, what is still open, and what is explicitly out of scope.

Getting help

Something not working, or a question? Open an issue at github.com/runboundai/runbound/issues, or email runboundai@gmail.com if it is not something you can post in public.

The two things that let us answer on the first reply instead of the third:

import runbound
print(runbound.__version__)
print(runbound.coverage())   # after init() and your first guarded call

coverage() is plain numbers and names — which provider classes were patched, how many calls and tools runbound saw, which imported providers nothing is guarding, and any silent-zero warning — so it shows at a glance whether runbound can see your traffic at all, which is the most common cause of "it isn't catching anything". It lists your decorated tools' names; remove any you would rather not post. Please also say your Python version, your openai / anthropic versions, and whether you run with a control plane (token / control_plane_url) or locally.

Never paste an API key, a control-plane token, a prompt, a reply, or a real session key into an issue.

Security issues go to email, not to an issue — see SECURITY.md for what to send and the disclosure window.

License

MIT — see LICENSE. Release history is in CHANGELOG.md. The promises above, stated as bounds with the tests that assert them (and where nothing does yet), are in INVARIANTS.md.

Metadata

Release files for runbound 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for runbound 0.3.0
File Size Uploaded
runbound-0.3.0.tar.gz 434.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for runbound 0.3.0
File Interpreter ABI Platform
runbound-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 659.1 kB

Release files / runbound-0.3.0.tar.gz

Download URL runbound-0.3.0.tar.gz
Size 434.1 kB
Tags Source
SHA-256 checksum
How to use checksums
9d1bcb3d326374938234a991fbbba94fd4b504f3e7fdbe55f1c153ebd02da030
BLAKE2b-256 checksum
How to use checksums
9f54dfc6e72b8edd75971d8fa45ecb1d78d8b464e1ab3bfa9816628e6f7363bc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / runbound-0.3.0-py3-none-any.whl

Download URL runbound-0.3.0-py3-none-any.whl
Size 225.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8248b953e026e260d0191a5642059fcf1ffa356060cfbc5b23a6a7354dd762da
BLAKE2b-256 checksum
How to use checksums
e4d3f05cc54278f884b43bb762ac959fdf36825a05e4a7c6f9ce2880ba7fbb72
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

This release

0.3.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page