Governance wrapper for AI agents — check_action → execute → record_outcome against a Stryda control plane.
Project description
stryda-sdk (Python)
One-file wrapper that puts a Stryda governance check in front of any tool call your agent makes, records the outcome afterwards, and hands you back a verifiable attestation.
This is Stryda's Layer 3 enforcement path — for agents that cannot route through MCP (custom scripts, third-party frameworks you do not control, internal services). For MCP-native agents, use /api/mcp directly — the pipeline at backend/mcp/pipeline.py already governs every call.
See docs/system-architecture.md for the full picture.
Install
Not published to PyPI. Install from the repo:
pip install -e ./packages/stryda-sdk-python
# Optional — offline attestation verification
pip install -e "./packages/stryda-sdk-python[verify]"
Quick start
import os
import stripe
from stryda_sdk import StrydaClient, governed, PolicyDenied, PendingApproval
stryda = StrydaClient(
api_key=os.environ["STRYDA_API_KEY"],
base_url=os.environ.get("STRYDA_BASE_URL", "https://api.stryda.ai"),
)
stripe.api_key = os.environ["STRIPE_KEY"]
def refund(charge_id: str, cents: int):
try:
return governed(
stryda,
tool="stripe.create_refund",
action_type="payment_refund",
args={"charge_id": charge_id, "amount_cents": cents},
cost_estimate=cents / 100,
agent_id="billing-agent",
idempotency_key=f"refund:{charge_id}:{cents}",
execute=lambda: stripe.Refund.create(charge=charge_id, amount=cents),
)
except PolicyDenied as e:
print(f"Stryda denied refund: {e.reason}")
except PendingApproval as e:
print(f"Refund needs approval: {e.escalation_id}")
Manual two-step
check = stryda.check_action(
tool="internal.delete_customer",
action_type="hard_delete",
args={"customer_id": customer_id},
)
if check.decision != "authorized":
... # handle denied / escalated
import time
started = time.monotonic()
try:
my_api.delete(customer_id)
stryda.record_outcome(
tool="internal.delete_customer",
check_id=check.check_id,
outcome="success",
latency_ms=int((time.monotonic() - started) * 1000),
)
except Exception as e:
stryda.record_outcome(
tool="internal.delete_customer",
check_id=check.check_id,
outcome="error",
latency_ms=int((time.monotonic() - started) * 1000),
error=str(e),
)
raise
Verifying attestations offline
Every record_outcome response includes an EdDSA JWT. The public key lives at https://api.stryda.ai/api/.well-known/jwks.json. The SDK's verifier:
from stryda_sdk import fetch_jwks, verify_attestation
jwks = fetch_jwks("https://api.stryda.ai") # cache this
claims = verify_attestation(record.attestation, jwks, audience="mcp-governance")
# claims["audit_id"], claims["tool"], claims["decision"], claims["sub"] (user_id), …
No Stryda-specific format — any JWT library that understands EdDSA with Ed25519 will verify these tokens against the JWKS. PyJWT[crypto] works out of the box:
import jwt, httpx
jwks = httpx.get("https://api.stryda.ai/api/.well-known/jwks.json").json()
claims = jwt.decode(
record.attestation,
key=jwt.algorithms.OKPAlgorithm.from_jwk(
next(k for k in jwks["keys"] if k["kid"] == record.attestation_kid)
),
algorithms=["EdDSA"],
audience="mcp-governance",
)
Error model
| Exception | Meaning |
|---|---|
StrydaError |
Transport or HTTP error from the control plane. |
PolicyDenied |
check_action returned denied. Do not execute the tool. |
PendingApproval |
check_action returned escalated. Surface escalation_id to the user / agent loop. |
governed() raises the first two for you. If execute() raises, record_outcome is still called with outcome="error" before the exception propagates. If both the tool and record_outcome fail, the tool exception wins — a best-effort audit write should not shadow the real error.
What this SDK does not do
- It does not proxy your API calls. Your process still talks to OpenAI / Stripe / Twilio directly.
- It does not cache decisions. A
deniedpolicy at T0 might beauthorizedat T1 after policy edits. - It does not retry. Use the same
idempotency_keyon retries — the control plane will return the stored decision instead of re-escalating.
Runtime requirements
- Python 3.10+.
httpx>=0.26.cryptography>=42only if you useverify_attestation(extra:stryda-sdk[verify]).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stryda_sdk-0.1.0.tar.gz.
File metadata
- Download URL: stryda_sdk-0.1.0.tar.gz
- Upload date:
- Size: 10.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9cfde1becb1f028a21244b2b3a6182e5e158511db22e94397fbc694820826f1f
|
|
| MD5 |
b86ea482975ee62f51d98ed333cdc956
|
|
| BLAKE2b-256 |
30d092d1d2e95484b17739e170d0d318f97b0b91eaf54d23ee871fb45d4f8fd0
|
File details
Details for the file stryda_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: stryda_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2f2c59fa98491d0ef3f7603420be0cfb19e1abc758d40206ecf4b474f7789a2
|
|
| MD5 |
10d3865480db2ebf3ac9ed151a70e6ff
|
|
| BLAKE2b-256 |
b2b072cc84b2d25c9adc22819e59ba53b0b59f12c9692912c97389818b68ae14
|