Python SDK
The Python SDK is byte-for-byte interoperable with Go, TypeScript, and Rust.
Python 3.10+. Pure-Python with cryptography and pqcrypto as the only runtime dependencies.
Install
Section titled “Install”pip install ratify-protocol==1.0.0a6Or from source:
git clone https://github.com/identities-ai/ratify-protocolcd ratify-protocol/sdks/pythonpip install -e '.[dev]' # the `dev` extra includes pytestThree minutes, end to end
Section titled “Three minutes, end to end”import timeimport secrets
from ratify_protocol import ( generate_human_root, generate_agent, issue_delegation, sign_challenge, verify_bundle, PROTOCOL_VERSION, SCOPE_MEETING_ATTEND, SCOPE_MEETING_SPEAK, DelegationCert, ProofBundle,)
# 1. Alice generates her hybrid 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.cert = DelegationCert( cert_id=secrets.token_hex(8), 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=int(time.time()), expires_at=int(time.time()) + 7 * 24 * 3600,)issue_delegation(cert, alice_priv)
# 4. Verifier issues a challenge. Agent signs it.challenge = secrets.token_bytes(32)challenge_at = int(time.time())challenge_sig = sign_challenge(challenge, challenge_at, 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=challenge_at, challenge_sig=challenge_sig,)
# 6. Verifier runs the verifier.result = verify_bundle(bundle, required_scope=SCOPE_MEETING_ATTEND)
if result.valid: print(f"✓ Authorized: {result.agent_id}, granted: {result.granted_scope}")else: print(f"✗ Rejected: {result.identity_status} — {result.error_reason}")Verifier options
Section titled “Verifier options”from ratify_protocol import verify_bundle, IdentityStatus
result = verify_bundle( bundle, required_scope="meeting:attend", required_custom_scope=None, now_override=None, # Use real time (test only) revocation_list=signed_revocation_list,)
# Branch on identity status — fail-closedif result.identity_status == IdentityStatus.VALID: accept_request()elif result.identity_status == IdentityStatus.BAD_SIGNATURE: reject("tampered")elif result.identity_status == IdentityStatus.EXPIRED: reject("expired")elif result.identity_status == IdentityStatus.SCOPE_DENIED: reject("insufficient scope")elif result.identity_status == IdentityStatus.REVOKED: reject("revoked")elif result.identity_status == IdentityStatus.REPLAY: reject("challenge too old")Async usage
Section titled “Async usage”The signing functions are synchronous (they’re CPU-bound crypto, not I/O). If you’re integrating
with an asyncio event loop, wrap them with asyncio.to_thread:
import asyncio
result = await asyncio.to_thread(verify_bundle, bundle, required_scope="api:read")This frees the event loop while ML-DSA-65 verification runs (~5–10 ms on a modern CPU).
Type hints
Section titled “Type hints”Every public function is fully type-annotated. The SDK passes mypy --strict.
from ratify_protocol import ( DelegationCert, ProofBundle, HybridSignature, HybridPublicKey, HybridPrivateKey, HumanRoot, AgentIdentity, VerifyResult, GeoConstraint, TemporalConstraint, VersionConstraint, RevocationList,)Conformance
Section titled “Conformance”cd sdks/pythonpytestExpected output: 59 passed. The fixtures are loaded from ../../testvectors/v1/ and run
through each function in the SDK. Byte-identical to the Go reference output.
Where to next
Section titled “Where to next”- Protocol concepts — what the bytes mean
- Scopes — the canonical 52-scope vocabulary
- Constraints — geo / time / version gating