Skip to main content

Regent Control — Python SDK

Runtime policy enforcement for AI agent actions. Every action your agent takes — a refund, a wire, a DB write, an API call — is authorized first: allow / deny / escalate, with a short-lived scoped token, the human it's acting for, the intent, and an immutable audit record.

pip install regent-control

Requires Python 3.10+. The only dependency is httpx.

Two ways to integrate

1. Gate-direct — RegentControl (you hold the provider key)

Authorize the action, get a scoped token, perform it, report the outcome.

from regent_control import RegentControl, MandateExceeded, Escalated

control = RegentControl(api_key="rgnt_ctrl_…", agent_id="agent_refund_bot")

decision = control.authorize(
    tool="payments", action="refund.create",
    amount_usd=50, mandate_id="mnd_support_refunds",
    idempotency_key="T-8842:ch_aaa",          # a retry won't double-refund
    user_token=rep_id_token,                  # the rep's OIDC token — verified by the gate
    intent="refund the duplicate charge on ticket 8842",
    facts={"account_status": "active", "refund_to_original": True},
)

if not decision.allowed:
    ...                                        # decision.code / decision.reason
do_refund(scoped_token=decision.token)         # the token authorizes the downstream call
control.complete(decision.decision_id, status="success", downstream_ref=refund_id)

A deny is a normal return value, not an exception. Opt into raising:

decision.raise_for_status()                    # raises MandateExceeded / PolicyDenied / Escalated / …
# or
control.authorize_or_raise(tool="payments", action="refund.create", amount_usd=999)

2. Sidecar-routing — SidecarSession (the agent holds no key)

Point your normal HTTP call at the sidecar; it injects the real vaulted credential and forwards. A policy deny comes back as a typed error.

from regent_control import SidecarSession, MandateExceeded

sc = SidecarSession(base_url="http://localhost:8080",
                    user_token=rep_id_token, intent="refund duplicate charge")
try:
    resp = sc.call("payments", "refunds", method="POST",
                   json={"charge": "ch_aaa", "amount": 50},
                   facts={"account_status": "active", "refund_to_original": True},
                   idempotency_key="T-8842:ch_aaa")
    refund = resp.json()
except MandateExceeded as e:
    ...                                        # e.code, e.reason, e.decision_id

Both clients have async twins: AsyncRegentControl and AsyncSidecarSession.

Wrap a tool in one line — @guarded

from regent_control import RegentControl, guarded

control = RegentControl(api_key="rgnt_ctrl_…", agent_id="agent_refund_bot")

@guarded(control, tool="payments", action="refund.create",
         amount_arg="amount_usd", mandate_id="mnd_support_refunds")
def issue_refund(*, charge: str, amount_usd: float, scoped_token: str = "") -> str:
    return provider.refund(charge, amount_usd, token=scoped_token)

issue_refund(charge="ch_1", amount_usd=50)     # authorized → runs → completed
issue_refund(charge="ch_1", amount_usd=999)    # raises MandateExceeded; never runs

The decorator authorizes before the call, injects the scoped token (if the function declares scoped_token), and reports success/failed to close the audit. Pass per-call context via a control_context={...} keyword.

Verify the scoped token at your service edge — regent_control.verify

The complement to the client: your downstream service validates the gate-issued scoped JWT against Regent's JWKS and enforces the scope itself (least privilege at the edge — no shared static key, no trusting the caller).

pip install "regent-control[verify]"   # adds PyJWT
from regent_control.verify import TokenVerifier, ScopeError

verifier = TokenVerifier(jwks_url="https://control-api.regentprotocol.org/v1/control/.well-known/jwks.json")
scoped = verifier.verify(token, expected_tool="payments", expected_action="refund.create")
# raises TokenError (bad sig / expired / wrong issuer|audience) or ScopeError (wrong scope)
# scoped.agent_id / scoped.decision_id are audit-ready

See examples/verify_service.py for a FastAPI dependency.

Develop locally with zero prod — regent-control dev

regent-control dev --port 8009 --deny-over 50 --escalate-over 500
# point any client at  base_url="http://localhost:8009"

In tests, force a verdict with the in-process mock:

from regent_control import RegentControl, MandateExceeded
from regent_control.dev import run_mock_control

def test_over_limit_is_denied():
    with run_mock_control(deny_over=50) as base_url:
        control = RegentControl(api_key="dev", agent_id="a", base_url=base_url)
        d = control.authorize("stripe", "refund.create", amount_usd=999)
        assert d.code == "MANDATE_LIMIT_EXCEEDED"

Coding agents — regent-control hook (Claude Code, Codex, Hermes)

Claude Code runs shell hooks around every tool call. regent-control hook is those hooks: the money tool calls your agent makes (mcp__stripe__*, mcp__get4agent__buy, a curl to a payment API…) go through the Regent gate before they run, and every completed one leaves a signed receipt you can verify offline.

pip install regent-control
regent-control hook install claude-code --agent-id agt_… --key-file ~/.regent/control-key.txt
regent-control hook install codex          # same hooks, ~/.codex/hooks.json
regent-control hook install hermes         # pre_tool_call / post_tool_call shell hooks in ~/.hermes/config.yaml (needs pyyaml)
regent-control hook status [--target codex|hermes]

One rule file, one gate, four agents: Claude Code and Codex speak the same hook JSON; Hermes sends {tool_name, args, task_id} and reads {"decision": "block", "reason"}, which --format hermes translates (no context channel there, so observe-mode verdicts are log-only); OpenClaw uses the @regent-protocol/openclaw-regent plugin instead of a shell hook.

That writes three hooks into ~/.claude/settings.json (PreToolUse, PostToolUse, PostToolUseFailure, matcher mcp__.*) and a rule file at ~/.regent/hook.json:

{
  "mode": "observe",
  "agent_id": "agt_…",
  "mandate_id": "man_…",
  "rules": [
    {"match": "^mcp__stripe__.*", "amount": "amount", "divisor": 100, "currency": "currency", "payee": "customer"},
    {"match": "^mcp__get4agent__buy$", "amount": ["price", "amount"], "payee": "seller"},
    {"match": "^Bash$", "input_match": {"command": "api\\.stripe\\.com"}, "tool": "stripe", "action": "charge",
     "amount": "amount_cents", "divisor": 100, "currency": "const:USD"}
  ]
}
  • A tool call that matches no rule never touches the network.
  • observe (default): the gate decides and records, nothing is blocked; the verdict is shown to the agent as context. enforce: a deny or an escalation blocks the call with the gate's reason, and an unreachable gate fails closed (fail_open: true to change that).
  • An allow never bypasses Claude Code's own permission prompt — the gate adds a mandate check and evidence on top of the user's consent, it does not replace it.
  • PostToolUse closes the decision and saves the receipt to ~/.regent/receipts/<decision_id>.jwt; verify it with regent-verify (pip install regent-receipt-verify).
  • regent-control hook uninstall removes the hooks; the key is read from REGENT_CONTROL_KEY or the --key-file, never written into settings.json.

The same gate, from OpenClaw: the @regent-protocol/openclaw-regent plugin registers a before_tool_call hook with the identical rules (see plugins/openclaw-regent in this repo).

Typed errors

Exception Decision code Meaning
IdentityNotResolved IDENTITY_NOT_RESOLVED the agent id didn't resolve
AgentNotActive AGENT_NOT_ACTIVE the agent is suspended/revoked
PolicyDenied POLICY_DENIED a policy rule forbade it
ToolNotAllowed TOOL_NOT_ALLOWED the tool isn't in the agent's catalog
MandateNotFound MANDATE_NOT_FOUND a money action with no mandate
MandateExceeded MANDATE_LIMIT_EXCEEDED over the spend cap
RiskThresholdExceeded RISK_THRESHOLD_EXCEEDED risk score too high
VelocityExceeded VELOCITY_EXCEEDED too many decisions for this agent in the window; back off
DuplicateRequest DUPLICATE_REQUEST an identical money request was just allowed; pass an idempotency_key for a deliberate repeat
Escalated ESCALATION_REQUIRED needs a human approval (carries .escalation)

ControlError (transport/auth/5xx) and ControlNetworkError (never reached the plane) are always raised; everything above subclasses ControlDenied.

Request signing (optional)

Pass sign_requests=True to HMAC-sign the request body with the per-agent secret HMAC-SHA256(api_key, agent_id) (X-Agent-Signature). Identical to @regent/control-sdk (TypeScript), so an agent moves between the two unchanged.

Examples

The agent's own signing key

By default a request proves that the caller knows the organization's control key and named an agent id. With its own Ed25519 key an agent proves which agent called: Regent keeps only the public half, and the receipt names the key (assertion_kid).

regent-control keys generate                       # ~/.regent/agent-key.json, chmod 600
regent-control keys register --api-key rgnt_… --org <org_id> --agent <agent_id>
regent-control keys require  --api-key rgnt_… --org <org_id> --agent <agent_id> --on

From then on the hooks sign every decision request with it (the key file is picked up automatically), and so does the client when you pass it:

control = RegentControl(api_key=key, agent_id=agent_id, signing_key_file="~/.regent/agent-key.json")

The signature is a standard RFC 9421 HTTP Message Signature (Signature-Input, Signature, Content-Digest; tag regent-agent, keyid = the key's RFC 7638 thumbprint) covering the method, host, path and body. Anyone can verify it with any RFC 9421 library against the key Regent publishes for the agent at GET /v1/agents/{agent_id}/jwks.json or in the agent's DID document (verificationMethod, JsonWebKey2020). The same key works for Web Bot Auth and AAuth.

keys require --on makes the gate refuse this agent's unsigned requests. Rotate by registering a new key, moving the agent to it, then revoking the old one (console → agent → Signing keys, or DELETE …/keys/{kid}).

Enrol with an invite (no organization key on the agent's machine)

In the console (agent → Enrolment) mint a one-time code and run it where the agent lives:

regent-control enroll rgnt_inv_…

That generates the agent's own key here (or reuses ~/.regent/agent-key.json), proves possession, and writes ~/.regent/credentials.json: a control key bound to this agent only (rgnt_agt_…, the gate refuses it for any other agent) plus the agent id and the control URL. The hooks pick it up automatically; in code:

control = RegentControl.from_credentials()

Metadata

Release files for regent-control 0.10.1

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

Source distribution (sdist)

Source distribution for regent-control 0.10.1
File Size Uploaded Provenance Actions
regent_control-0.10.1.tar.gz 57.9 kB With provenance Details for regent_control-0.10.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for regent-control 0.10.1
File Interpreter ABI Platform Actions
regent_control-0.10.1-py3-none-any.whl Python 3 none any Details for regent_control-0.10.1-py3-none-any.whl

Total release size: 108.7 kB

Release files / regent_control-0.10.1.tar.gz

Download URL regent_control-0.10.1.tar.gz
Size 57.9 kB
Tags Source
SHA-256 checksum
How to use checksums
b89e42280deaf089356f6873a2b7bb23b9398148fbcadd0cf714821dbe125f83
BLAKE2b-256 checksum
How to use checksums
caa0e6f44a718ba89040326fbd3af13584263401503f3378da0aa503a37382a2
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 Oct 15, 2026.

Transparency log

Release files / regent_control-0.10.1-py3-none-any.whl

Download URL regent_control-0.10.1-py3-none-any.whl
Size 50.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
34047420076f10348a8b9acf9609c1fb0b0246ae87770e5727966787d163956a
BLAKE2b-256 checksum
How to use checksums
8a8dc9f898c5ad52ee406ace5419b1e0e54e5c42c06fe0951ac008e265989508
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 Oct 15, 2026.

Transparency log

Security for regent-control 0.10.1

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for regent_control-0.10.1.tar.gz

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 Oct 15, 2026.

Transparency log

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.

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