Protect a production deployment with BobSentry
Canonical ~10-minute tutorial. An agent intends deploy_production, BobSentry returns ALLOW / REQUIRE_APPROVAL / BLOCK, and your integration honors that decision. A fake local deploy runs only when authorization conditions are satisfied. BobSentry never performs the deployment.
Trust boundary
BobSentry returns the authorization decision. Your integration is responsible for honoring that decision and performing or withholding the downstream action.
Why deploy_production?
Production deploy is a clear consequential execution boundary: if the integrator runs it without authorization, systems change. The same model applies to other high-impact actions (this page does not tutorialize them): PR merge, infrastructure mutation, database/schema changes, IAM changes, and consequential tool calls.
Prerequisites
- A BobSentry organization / account
- Create an Agent (see Software Agents)
- Create / reveal an Agent-bound API credential
- Note the Agent ID (must match the credential's
boundAgentIdwhen present) - Choose the Python or TypeScript SDK below
Install
Public packages: bobsentry 0.4.0 on PyPI (Python ≥3.12) and @bobsentry/sdk 0.1.0 on npm (Node ≥20, ESM).
Python
pip install bobsentry # Optional pin: # pip install bobsentry==0.4.0
TypeScript
npm install @bobsentry/sdk # or pnpm add @bobsentry/sdk
Environment
export BOBSENTRY_API_KEY="bsk_..." export BOBSENTRY_AGENT_ID="infra-agent"
Decision model
ALLOW— integrator may proceed.REQUIRE_APPROVAL— do not execute yet. A human must authorize in the BobSentry console.BLOCK— do not execute. No override, no auto-retry.
Authorization ≠ execution ≠ outcome. Customer agent modes are COOPERATIVE and OBSERVE. Unknown / unmatched consequential actions retain a safe REQUIRE_APPROVAL fallback.
Approval semantics (critical)
REQUIRE_APPROVAL is the original authorization decision. Human approval satisfies that requirement; the decision record remains REQUIRE_APPROVAL and approval_status becomes APPROVED. That combination allows the integrator to proceed. Do not rewrite REQUIRE_APPROVAL to ALLOW in your code or docs.
| State | decision | approval_required | approval_status | May proceed? |
|---|---|---|---|---|
| ALLOW | ALLOW | false | NOT_REQUIRED | Yes |
| Pending approval | REQUIRE_APPROVAL | true | PENDING | No |
| Approved | REQUIRE_APPROVAL | true | APPROVED | Yes |
| Denied | REQUIRE_APPROVAL | true | DENIED | No |
| Expired | REQUIRE_APPROVAL | true | EXPIRED | No |
| Blocked | BLOCK | — | — | No |
Proceed when: decision == ALLOW or (decision == REQUIRE_APPROVAL and approval_status == APPROVED).
After an approved REQUIRE_APPROVAL, SDK helpers stay requires_approval / requiresApproval = true and is_allowed / isAllowed = false. Do not use is_allowed / isAllowed as the post-approval proceed condition.
Python
Uses BobSentry.authorize and get_decision. Repo example: bobsentry-runtime/sdk/python/examples/authorize_deploy_production.py
import os
from bobsentry import BobSentry
bob = BobSentry(
api_key=os.environ["BOBSENTRY_API_KEY"],
agent_id=os.environ["BOBSENTRY_AGENT_ID"],
)
def fake_deploy() -> None:
print("FAKE DEPLOYMENT: no production system was changed")
decision = bob.authorize(
action="deploy_production",
action_environment="production",
context={"service": "payments"},
)
print(f"decision={decision.decision} id={decision.decision_id}")
if decision.is_allowed:
fake_deploy()
elif decision.requires_approval:
print("REQUIRE_APPROVAL: do not deploy yet.")
print(f"Approve in the BobSentry console, then check: {decision.decision_id}")
# Preferred 10-minute path: approve in the UI, then call get_decision once.
# Do not treat is_allowed as true after approval — it stays false.
latest = bob.get_decision(decision.decision_id)
if (
latest.decision == "REQUIRE_APPROVAL"
and latest.approval_status == "APPROVED"
):
fake_deploy()
else:
print(f"Still waiting or denied: approval_status={latest.approval_status}")
elif decision.is_blocked:
print("BLOCK: do not deploy.")
if decision.reasonMessage:
print(f"reason: {decision.reasonMessage}")TypeScript
Uses bob.authorize and bob.getDecision. Requires Node ≥20 and ESM (import / "type": "module"; CommonJS is not supported). Repo example: bobsentry-runtime/sdk/typescript/examples/authorize-deploy-production.ts
import { BobSentry } from "@bobsentry/sdk";
const bob = new BobSentry({
apiKey: process.env.BOBSENTRY_API_KEY!,
agentId: process.env.BOBSENTRY_AGENT_ID!,
});
function fakeDeploy(): void {
console.log("FAKE DEPLOYMENT: no production system was changed");
}
const decision = await bob.authorize({
action: "deploy_production",
actionEnvironment: "production",
context: { service: "payments" },
});
console.log(`decision=${decision.decision} id=${decision.decisionId}`);
if (decision.isAllowed) {
fakeDeploy();
} else if (decision.requiresApproval) {
console.log("REQUIRE_APPROVAL: do not deploy yet.");
console.log(`Approve in the BobSentry console, then check: ${decision.decisionId}`);
// Preferred 10-minute path: approve in the UI, then call getDecision once.
// Do not wait for isAllowed to become true — it stays false after approval.
const latest = await bob.getDecision(decision.decisionId);
if (
latest.decision === "REQUIRE_APPROVAL" &&
latest.approvalStatus === "APPROVED"
) {
fakeDeploy();
} else {
console.log(`Still waiting or denied: approvalStatus=${latest.approvalStatus}`);
}
} else if (decision.isBlocked) {
console.log("BLOCK: do not deploy.");
if (decision.reasonMessage) {
console.log(`reason: ${decision.reasonMessage}`);
}
}Preferred 10-minute path
- Run
authorizefordeploy_production. - If
REQUIRE_APPROVAL, open Approvals and approve the ticket. - Call
get_decision/getDecisiononce and proceed only whenREQUIRE_APPROVAL+APPROVED(or originalALLOW).
Long-running wait loops are optional advanced usage (see the Python SDK wait_for_approval helpers). They are not required for this tutorial.
Next steps
- Runtime Integration Contract
- Software Agents / credential setup
- Approval Workflows
- Audit Evidence (verify signed records after you have a working authorize loop)