Skip to main content

bulwark-sdk (Python)

Governance, audit and policy decisions for AI agents.

Zero runtime dependencies — stdlib only. Python 3.9+.

Install

Not yet on PyPI. Install from git:

pip install bulwark-sdk

If that fails with "No matching distribution found", the package is not on PyPI yet. Two alternatives, both fully supported:

  • Use the REST API directly. Three calls, no install. See docs/API.md §2.
  • Vendor it. Copy src/bulwark/ into your project. Zero dependencies, nothing to install.

The one thing to understand first

There are two calls, and they do different jobs:

Call Endpoint What happens Counts as a decision?
observe() POST /v1/events Appended to the audit chain. No policy is evaluated. Buffered + batched. No
decide() POST /v1/decide Policy is evaluated. Returns allow/deny/escalate, can raise a human approval. Yes

If you only call observe(), your dashboard shows activity but zero decisions, zero flags, and a risk score driven only by registry facts. That is working as designed. Call decide() at the point your agent is about to do something that matters.

Bulwark never blocks your agent. decide() returns a verdict; acting on it is your code's job.

Quickstart

from bulwark import BulwarkClient

bw = BulwarkClient(api_key="bw_live_...")  # Settings → API keys

# 1. Register once (at deploy time, not per request)
agent = bw.register_agent(
    name="Portfolio Assistant",
    purpose="Answers investor questions about portfolio performance",
    risk_tier="medium",
    environment="prod",
    tools=["portfolio.read", "ai.assistant.answer"],
)
bw.activate_agent(agent["agentId"])   # agents are created "pending"

# 2. Record what the agent did
bw.observe(agent["agentId"], "ai.assistant.answer", resource="portfolio")

# 3. Ask for a verdict before something consequential
verdict = bw.decide(agent["agentId"], "crm.write", resource="customers")
if verdict.pending:
    log.warning("Awaiting human approval: %s", verdict.approval_id)
elif not verdict.allowed:
    log.warning("Bulwark would block this: %s", verdict.reason)

Three things that surprise people

1. Agents start pending, not active. decide() denies anything from a non-active agent with agent_not_active. Call activate_agent() after registering.

2. tools is currently descriptive, not enforced. Declaring tools=["crm.read"] records the agent's intended allowlist and drives the access-matrix view, but Bulwark does not yet reject or escalate an action outside it. Today this returns allow:

bw.register_agent(..., tools=["crm.read"])
bw.decide(agent_id, "crm.write")   # -> allow, "no matching policy — default allow"

To enforce scope today, author an explicit policy (POST /v1/policies) with a rule matching the actions you want escalated. Declare tools anyway — it is the allowlist scope enforcement will read once it ships.

3. Events for an unregistered agent are dead-lettered, not rejected. A typo'd agent_id fails quietly — the event lands in /v1/events/dead-letter rather than erroring. The SDK emits a warning when the API reports dead-lettered events.

Serverless

The background flusher does not run once your process is frozen or torn down. Flush before you return:

def handler(event, context):
    bw.observe(agent_id, "doc.process", resource=event["key"])
    ...
    bw.flush()      # or: with BulwarkClient(...) as bw:
    return {"ok": True}

flush() guarantees that everything buffered when you called it has been sent before it returns.

Failure behaviour

fail_open=True (the default) is the monitor-mode contract: governance being down must never take your agent down.

  • observe() never raises. Failed batches are dropped and counted in bw.dropped_events.
  • decide() returns an allow marked degraded=True. Never treat a degraded verdict as a real governance record — it means no policy was evaluated.

Set fail_open=False to surface errors as exceptions instead.

Don't send content

Never pass prompts, completions, or document bodies in context. Bulwark strips content fields at ingest by design, but the cheapest data to protect is the data you never transmit. Send metadata: identifiers, action names, resource names, counts.

Configuration

BulwarkClient(
    api_key,
    base_url="https://bulwark-api.fly.dev",
    timeout=5.0,
    batch_size=100,       # API hard cap is 100 events per batch
    flush_interval=2.0,   # background flush cadence, seconds
    fail_open=True,
    max_queue=10_000,     # buffer bound; oldest dropped beyond it
    max_retries=3,        # 429 and 5xx only, honours retry-after
)

API

Method Purpose
register_agent(name, purpose, risk_tier, environment, *, tools, data_sources, models_used, scope, category, owner_user_id, spend_limit_gbp) Register. Returns the agent dict (state pending).
activate_agent(agent_id) Transition to active.
set_agent_state(agent_id, state) draft→pending→active→suspended/retired.
list_agents() All agents for your tenant.
observe(agent_id, action, *, resource, on_behalf_of, context, type) Buffered telemetry.
decide(agent_id, action_type, *, resource, data_classification, context) Policy verdict.
flush(timeout=None) Send everything buffered.
close() Flush and stop the worker. Runs at exit automatically.

Exceptions

All inherit BulwarkError. BulwarkAuthError (401), BulwarkPermissionError (403), BulwarkUpgradeRequired (402, has .feature), BulwarkRateLimited (429, has .retry_after), BulwarkPayloadTooLarge (413), BulwarkValidationError (400, .body["details"]), BulwarkTransportError (network).

An API key maps to the developer role: it can register agents, send events and call decide(), but cannot mint keys, manage team members, or change the plan — those need an admin session.

Tests

pip install -e ".[dev]"
python -m pytest

Metadata

Release files for bulwark-sdk 0.1.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 bulwark-sdk 0.1.0
File Size Uploaded Provenance
bulwark_sdk-0.1.0.tar.gz 17.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for bulwark-sdk 0.1.0
File Interpreter ABI Platform
bulwark_sdk-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.3 kB

Release files / bulwark_sdk-0.1.0.tar.gz

Download URL bulwark_sdk-0.1.0.tar.gz
Size 17.0 kB
Tags Source
SHA-256 checksum
How to use checksums
e3e1aa9b28b985c5489a69c1c5067edaa9db868550a526bc11470ed2e887203c
BLAKE2b-256 checksum
How to use checksums
14632bbb3616987f523aaf44b6718185082e0b11be36569d929f511f7d935fe3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release files / bulwark_sdk-0.1.0-py3-none-any.whl

Download URL bulwark_sdk-0.1.0-py3-none-any.whl
Size 13.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
87d6216b68b19fbc59a871dad2e7551c255996b49d2d89dea67cd52927bd64cd
BLAKE2b-256 checksum
How to use checksums
5d7343340b5220a941584c49c06d0b112a7c2629adda8c68d950f6d3415ea2df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Security for bulwark-sdk 0.1.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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