Python SDK
The Python SDK is byte-for-byte interoperable with Go, TypeScript, and Rust. Python 3.10+.
Runtime dependencies are cryptography (Ed25519) and pqcrypto (ML-DSA-65) — both audited,
both maintained by mainstream Python crypto teams.
Stability: every primitive on this page is stable in 1.0.0a7. There are no preview /
experimental APIs in the snippets below.
Install
Section titled “Install”pip install ratify-protocol==1.0.0a7The package on PyPI is ratify-protocol. The module you import is ratify_protocol (PEP 8
naming).
Three minutes, end to end
Section titled “Three minutes, end to end”import timefrom ratify_protocol import ( PROTOCOL_VERSION, SCOPE_MEETING_ATTEND, SCOPE_MEETING_SPEAK, DelegationCert, ProofBundle, VerifyOptions, generate_agent, generate_challenge, generate_human_root, issue_delegation, sign_challenge, verify_bundle,)
# 1. Alice generates her hybrid (Ed25519 + ML-DSA-65) root identity.alice, alice_priv = generate_human_root()
# 2. Her AI agent generates its own hybrid keypair.agent, agent_priv = generate_agent("Alice's Scheduler", "custom")
# 3. Alice signs a delegation cert for the agent.now = int(time.time())cert = DelegationCert( cert_id="cert-001", version=PROTOCOL_VERSION, issuer_id=alice.id, issuer_pub_key=alice.public_key, subject_id=agent.id, subject_pub_key=agent.public_key, scope=[SCOPE_MEETING_ATTEND, SCOPE_MEETING_SPEAK], issued_at=now, expires_at=now + 7 * 24 * 3600, signature=None, # populated in place by issue_delegation)issue_delegation(cert, alice_priv)
# 4. Verifier issues a challenge. Agent signs it.challenge = generate_challenge()challenge_sig = sign_challenge(challenge, now, agent_priv)
# 5. Agent assembles a proof bundle.bundle = ProofBundle( agent_id=agent.id, agent_pub_key=agent.public_key, delegations=[cert], challenge=challenge, challenge_at=now, challenge_sig=challenge_sig,)
# 6. Verifier runs the verifier in a single call.result = verify_bundle( bundle, VerifyOptions(required_scope=SCOPE_MEETING_ATTEND),)
if result.valid: print(f"✓ Authorized") print(f" human_id: {result.human_id}") print(f" agent_id: {result.agent_id}") print(f" granted_scope: {result.granted_scope}") print(f" identity_status: {result.identity_status}")else: print(f"✗ Rejected: {result.identity_status} — {result.error_reason}")Real output (IDs are derived from the freshly generated keys, so they’ll differ on every run):
✓ Authorized human_id: b3ef7456eb0a741008ee97cb30ee4aec agent_id: 98f087953d2da9d52e78f02300df380d granted_scope: ['meeting:attend', 'meeting:speak'] identity_status: authorized_agentVerifier-side: branching on identity_status
Section titled “Verifier-side: branching on identity_status”verify_bundle always returns a VerifyResult. Inspect result.valid first; if it’s
false, the specific failure mode is in result.identity_status (a literal string,
matching the byte-for-byte protocol-level enum across all four SDKs):
status = result.identity_status
if status == "authorized_agent": # ✓ All checks passed. result.granted_scope is the intersection of # every cert's scope (effective scope across the chain). passelif status == "expired": # ✗ At least one cert is past its expires_at OR not-yet-valid. passelif status == "revoked": # ✗ A cert ID in the chain matched a revoked entry from the # revocation provider (or the legacy is_revoked closure). passelif status == "scope_denied": # ✗ required_scope was not in the chain's effective (intersected) # scope, OR an advanced PolicyProvider rejected the bundle. passelif status == "constraint_denied": # ✗ A first-class constraint (geo/time/amount/rate) evaluated to # "outside the allowed range". passelif status == "constraint_unverifiable": # ✗ A constraint required input the caller didn't provide # (e.g. cert has geo_circle but VerifierContext.current_lat was None). passelif status == "constraint_unknown": # ✗ A cert declared a constraint type this SDK build doesn't recognize. passelif status == "delegation_not_authorized": # ✗ An intermediate cert sub-delegated without identity:delegate. passelif status == "invalid": # ✗ Catch-all for structural / cryptographic failures. # result.error_reason carries a stable machine-readable prefix # (e.g. "stale_challenge: challenge is 31 seconds old (max 300)"). passThe verifier is fail-closed by default — any error path returns valid=False.
Constraints (geo / time / amount / rate / speed)
Section titled “Constraints (geo / time / amount / rate / speed)”When a delegation declares constraints, supply the runtime context:
from ratify_protocol import VerifierContext
ctx = VerifierContext( current_lat=37.7749, current_lon=-122.4194,)
result = verify_bundle( bundle, VerifyOptions( required_scope="drone:deliver", context=ctx, ),)Unset fields (None) on VerifierContext mean “not supplied” — any geo_circle /
geo_polygon / geo_bbox constraint will return constraint_unverifiable if you don’t
set both current_lat and current_lon.
The full constraint vocabulary (geo_circle, geo_polygon, geo_bbox, time_window,
max_speed_mps, max_amount, max_rate) and VerifierContext field requirements are
documented in Constraints.
Sub-delegation
Section titled “Sub-delegation”Agent-to-agent delegation uses the same primitive. Alice grants Agent A meeting:attend
plus the identity:delegate privilege; A then issues a sub-delegation to Agent B.
Without identity:delegate on A’s grant from Alice, A’s sub-delegation will be rejected
with delegation_not_authorized.
from ratify_protocol import SCOPE_IDENTITY_DELEGATE
# Alice → A: meeting:attend + identity:delegatealice_to_a = DelegationCert( cert_id="cert-alice-to-a", version=PROTOCOL_VERSION, issuer_id=alice.id, issuer_pub_key=alice.public_key, subject_id=agent_a.id, subject_pub_key=agent_a.public_key, scope=[SCOPE_MEETING_ATTEND, SCOPE_IDENTITY_DELEGATE], issued_at=now, expires_at=now + 86400, signature=None,)issue_delegation(alice_to_a, alice_priv)
# A → B: a subset of A's granta_to_b = DelegationCert( cert_id="cert-a-to-b", version=PROTOCOL_VERSION, issuer_id=agent_a.id, issuer_pub_key=agent_a.public_key, subject_id=agent_b.id, subject_pub_key=agent_b.public_key, scope=[SCOPE_MEETING_ATTEND], issued_at=now, expires_at=now + 3600, signature=None,)issue_delegation(a_to_b, agent_a_priv)
# Chain order: [leaf, ..., root]. A→B is the leaf, Alice→A is the root.bundle = ProofBundle( agent_id=agent_b.id, agent_pub_key=agent_b.public_key, delegations=[a_to_b, alice_to_a], challenge=challenge, challenge_at=challenge_at, challenge_sig=agent_b_sig,)
result = verify_bundle( bundle, VerifyOptions(required_scope=SCOPE_MEETING_ATTEND),)result.granted_scope is the lex-sorted intersection of every cert in the chain.
identity:delegate doesn’t propagate to B because A didn’t grant it onward — that’s the
intentional behavior.
Provider hooks (SPEC §17)
Section titled “Provider hooks (SPEC §17)”The provider hooks land on VerifyOptions as Optional[...] fields:
from ratify_protocol import ( VerifyOptions, RevocationProvider, PolicyProvider, AuditProvider, ConstraintEvaluator, AnchorResolver,)
result = verify_bundle( bundle, VerifyOptions( required_scope="meeting:attend", revocation=my_revocation_provider, # §17.1 policy=my_policy_provider, # §17.2 audit=my_audit_provider, # §17.3 constraint_evaluators=my_ext_registry, # §17.7 anchor_resolver=my_anchor_resolver, # §17.8 policy_verdict=cached_verdict, # §17.6 policy_secret=policy_secret, context=ctx, ),)RevocationProvider, PolicyProvider, AuditProvider, ConstraintEvaluator, and
AnchorResolver are typing.Protocol classes — duck-typing applies. Any class
implementing the expected method signature works; no inheritance required.
Each hook has fail-closed semantics. See Provider architecture for the full reference.
Running the conformance suite
Section titled “Running the conformance suite”cd ratify-protocol/sdks/pythonpip install -e ".[dev]"pytest -qYou should see all 59 canonical wire-format fixtures plus the 10 alpha.7 cross-SDK byte-equivalence vectors pass. If any fail, the bytes have drifted from the reference and the SDK is not interoperable — file a bug.
Where to next
Section titled “Where to next”- Protocol concepts: Delegate → Present → Verify — every primitive in depth
- Constraints — full constraint vocabulary +
VerifierContextreference - Provider architecture — the §17 hooks plus the alpha.7 levers (
VerificationReceipt,PolicyVerdict) - Integration guides — surface-specific patterns