Skip to content

IDProva launches April 7 — Registry packages coming at launch. Build from source now.

Python SDK

The IDProva Python SDK provides native Rust performance through PyO3 bindings. All cryptographic operations run in Rust — Python gets a safe, typed API with zero-copy where possible.

Terminal window
pip install idprova

Requirements: Python 3.9+. Pre-built wheels for Linux (manylinux), macOS (x86 + ARM), and Windows.

  1. Create an agent identity

    from idprova import AgentIdentity
    identity = AgentIdentity.create("my-agent", domain="example.com")
    print(identity.did) # did:idprova:example.com:my-agent
  2. Issue a delegation token

    dat = identity.issue_dat(
    "did:idprova:example.com:sub-agent",
    ["mcp:tool:read", "mcp:resource:docs:write"],
    expires_in_seconds=3600,
    )
    print(dat.to_compact()) # JWS compact serialization
  3. Verify the token

    verified = dat.verify_signature(identity.public_key_bytes)
    dat.validate_timing() # raises if expired
    print(f"Scopes: {dat.scope}")

High-level convenience class — the easiest way to get started.

from idprova import AgentIdentity
# Create with auto-generated Ed25519 keypair
identity = AgentIdentity.create(
"my-agent",
domain="example.com", # default: "local.dev"
controller="did:idprova:example.com:alice", # optional
)
# Access components
aid = identity.aid() # AID document
keypair = identity.keypair() # Ed25519 KeyPair
did = identity.did # "did:idprova:example.com:my-agent"
# Issue delegation tokens
dat = identity.issue_dat(
"did:idprova:example.com:sub-agent",
["mcp:tool:*:read"],
expires_in_seconds=3600,
)

Ed25519 key pair for signing and verification. Private keys are held in Rust memory and never exposed to Python.

from idprova import KeyPair
# Generate a new keypair
kp = KeyPair.generate()
# Sign and verify
sig = kp.sign(b"hello world")
assert kp.verify(b"hello world", sig)
# Access public key
print(kp.public_key_multibase) # z6Mk... (base58btc)
print(len(kp.public_key_bytes)) # 32
# Restore from saved secret (32 bytes)
kp2 = KeyPair.from_secret_bytes(secret_bytes)

Create W3C DID Documents with agent metadata.

from idprova import AIDBuilder, KeyPair
kp = KeyPair.generate()
builder = AIDBuilder()
builder.id("did:idprova:example.com:my-agent")
builder.controller("did:idprova:example.com:alice")
builder.name("My Agent")
builder.description("A research assistant agent")
builder.model("anthropic/claude-sonnet-4-5")
builder.runtime("python/3.12")
builder.trust_level("L0")
builder.add_ed25519_key(kp)
aid = builder.build()
# Serialize / deserialize
json_str = aid.to_json()
aid2 = AID.from_json(json_str)
aid2.validate() # raises on invalid structure

Signed, scoped, time-bounded permission tokens.

from idprova import DAT, KeyPair
issuer_kp = KeyPair.generate()
dat = DAT.issue(
issuer_did="did:idprova:example.com:alice",
subject_did="did:idprova:example.com:my-agent",
scope=["mcp:tool:filesystem:read", "mcp:tool:filesystem:write"],
expires_in_seconds=86400, # 24 hours
signing_key=issuer_kp,
max_actions=1000, # optional constraint
require_receipt=True, # optional: require audit receipts
)
# Serialize to JWS compact format
compact = dat.to_compact()
# Parse and verify
dat2 = DAT.from_compact(compact)
dat2.verify_signature(issuer_kp.public_key_bytes)
dat2.validate_timing()
# Inspect claims
print(dat2.issuer) # did:idprova:example.com:alice
print(dat2.subject) # did:idprova:example.com:my-agent
print(dat2.scope) # ["mcp:tool:filesystem:read", ...]
print(dat2.jti) # unique token ID
print(dat2.is_expired) # False
print(dat2.expires_at) # Unix timestamp

Permission scope validation with wildcard support.

from idprova import Scope
parent = Scope("mcp:tool:filesystem:*")
child = Scope("mcp:tool:filesystem:read")
assert parent.covers(child) # True — wildcard matches
assert not child.covers(parent) # False — can't escalate

Trust level comparison and validation.

from idprova import TrustLevel
l1 = TrustLevel("L1")
l2 = TrustLevel("L2")
assert l2.meets_minimum(l1) # True — L2 >= L1
assert not l1.meets_minimum(l2) # False — L1 < L2
print(l1.description) # Human-readable description

Append-only, hash-chained audit log.

from idprova import ReceiptLog
log = ReceiptLog()
print(log.last_hash) # "genesis"
print(log.next_sequence) # 0
print(len(log)) # 0
# Verify chain integrity
log.verify_integrity() # raises if tampered
# Serialize
json_str = log.to_json()

The SDK ships with PEP 561 type stubs (.pyi files). All classes have full type annotations for IDE autocompletion and mypy/pyright checking.

# mypy and pyright will catch type errors:
dat = DAT.issue(
issuer_did="did:idprova:example.com:alice",
subject_did="did:idprova:example.com:agent",
scope=["mcp:tool:read"],
expires_in_seconds="not a number", # Type error!
signing_key=kp,
)
  • Private keys never leave Rust memory — Python only holds opaque references
  • DAT.from_compact() rejects tokens with alg: "none" (SEC-3 algorithm confusion)
  • All cryptographic operations use audited ed25519-dalek via Rust
  • The SDK is compiled with opt-level=3 for production performance