BobSentry logoBobSentry

Python SDK Quickstart

Public package bobsentry 0.4.0. Install with pip install bobsentry (Python ≥3.12). The SDK calls POST /v1/runtime/evaluate with x-api-key, then honors ALLOW / REQUIRE_APPROVAL / BLOCK in your process. BobSentry returns the authorization decision. Your integration is responsible for honoring that decision and performing or withholding the downstream action. Canonical HTTP contract: Runtime Integration Contract. Start with the production deploy quickstart for the end-to-end authorize → approve → proceed path.

Install

pip install bobsentry
# Optional pin:
# pip install bobsentry==0.4.0

Requires Python 3.12+.

Authorize (public client)

Preferred public API: BobSentry.authorize and get_decision. Does not wait for approval and does not run the downstream action.

from bobsentry import BobSentry

bob = BobSentry(
    api_key="bobsentry_live_xxx",
    agent_id="infra-agent",
    base_url="https://runtime.bobsentry.com",
)

decision = bob.authorize(
    action="deploy_production",
    action_environment="production",
    context={"service": "payments"},
)

# Proceed only on ALLOW, or REQUIRE_APPROVAL + approval_status APPROVED.
# Approved REQUIRE_APPROVAL does not become ALLOW.
if decision.is_allowed:
    ...
elif decision.requires_approval:
    latest = bob.get_decision(decision.decision_id)
    if latest.decision == "REQUIRE_APPROVAL" and latest.approval_status == "APPROVED":
        ...
elif decision.is_blocked:
    ...

Protect a function (Sentry guard)

Compatibility path: decorator-based protection with the same runtime evaluate contract.

from bobsentry import Sentry, SdkConfig

sentry = Sentry(
    api_key="bobsentry_live_xxx",
    agent_id="ehr-agent",
    base_url="https://runtime.bobsentry.com",
    config=SdkConfig(fail_mode="closed"),  # closed | open | observe
)

@sentry.guard(action="send_external_email", target="external_email")
def send_email(to: str, body: str) -> None:
    # BobSentry evaluates BEFORE this body runs. If the decision is not ALLOW,
    # this line never executes.
    smtp_send(to, body)

What happens on each call

  • - The SDK classifies args locally and sends metadata only to /v1/runtime/evaluate (authenticated with x-api-key). Raw payloads never leave your environment.
  • - The runtime returns a signed decision: ALLOW, REQUIRE_APPROVAL, or BLOCK. Authorization ≠ execution ≠ outcome.
  • - On ALLOW, the SDK runs your function. On REQUIRE_APPROVAL / BLOCK, it raises (unless you opt into approval polling). The SDK — not BobSentry — performs any local execution.

Async is supported

@sentry.guard(action="deploy_production", target="prod-cluster")
async def deploy(service: str, version: str) -> str:
    return await do_deploy(service, version)

Protect an existing tool

Framework-agnostic, no arg serialization, preserves callable metadata.

from bobsentry import Sentry
from bobsentry.integrations import protect_tool

sentry = Sentry(api_key="...", agent_id="ops-agent")

# Wrap any existing sync or async callable (a plain function, a LangChain
# StructuredTool.func, an OpenAI tool dispatcher, ...). protect_tool never
# serializes the tool's args/kwargs into the request.
safe_deploy = protect_tool(sentry, deploy, action="deploy_production")

Wait for approval (optional)

Opt in per guard. The SDK polls only the existing decision id and never re-runs authorization. A human approves in the console; a tenant key cannot self-approve.

@sentry.guard(
    action="run_database_migration",
    target="prod-db",
    wait_for_approval=True,
    approval_timeout_s=600,
    approval_poll_interval_s=5,
)
def run_migration(migration_id: str) -> str:
    return apply(migration_id)

# APPROVED -> runs exactly once
# DENIED   -> ExecutionDeniedError
# EXPIRED  -> ApprovalExpiredError
# timeout  -> ApprovalTimeoutError (ticket may still be PENDING server-side)
# NOT_REQUIRED / unknown / malformed -> ApprovalProtocolError (never executes)

Local simulation (no key, no network)

# Offline: no API key, no network. Explore decisions before wiring the runtime.
from bobsentry import Sentry

sentry = Sentry(agent_id="dev-agent", simulate=True)   # canonical activation
sentry.simulation_engine.add_policy("external_api_call", "REQUIRE_APPROVAL")

@sentry.guard(action="delete_repository")
def wipe(name: str) -> None:
    ...  # simulated decision is BLOCK -> ExecutionBlockedError

# Simulated decisions are NON-PRODUCTION: source=LOCAL_SIMULATION,
# signed_evidence=False, policy_id begins with "LOCAL-SIM-".

Verify signed evidence

bobsentry verify ./evidence-bundle.json
# {"verified": true, "checks": [...]}   (local-simulation records are rejected)

See the Runtime Integration Contract, developer overview, or the audit evidence guide for more.

Python SDK Quickstart | BobSentry Docs | BobSentry