Post-quantum cryptographic identity for AI agents
Project description
cordprotocol
Post-quantum cryptographic identity for AI agents — Python SDK
Cord Protocol gives every AI agent a cryptographically signed identity credential so that tools, services, and other agents can verify who is calling before acting. The Python SDK is a first-class implementation designed for developers building LangChain, AutoGen, and CrewAI agents.
TypeScript developer?
The JavaScript/TypeScript SDK is@cordprotocol/sdkon npm.
Both SDKs share the same credential schema, so credentials are mutually inspectable across languages.
Installation
pip install cordprotocol
Requires Python 3.9+ and depends only on cryptography and pydantic.
Quick start
import os
from cordprotocol import CordProtocol, CordProtocolConfig, generate_keypair, SCOPES
kp = generate_keypair() # generate once, store securely
cord = CordProtocol(CordProtocolConfig(
registry=True,
api_key=os.environ.get("CORD_API_KEY"),
))
# Issue a signed credential — automatically registered in the public trust network
cred = cord.issue_credential(
agent_id="my-agent-001",
issued_to="acme-corp",
permissions=[SCOPES.READ, SCOPES.EXECUTE],
expires_in="24h",
private_key=kp.private_key,
)
# Verify signature, expiry, and revocation status
result = cord.verify_credential(cred)
assert result.valid
print(result.credential.agent_id) # "my-agent-001"
API reference
generate_keypair(backend=None) -> KeyPair
Generate a new cryptographic keypair.
from cordprotocol import generate_keypair
kp = generate_keypair()
print(kp.algorithm) # "Ed25519"
print(kp.public_key) # base64-encoded public key
print(kp.private_key) # base64-encoded private key (keep secret)
| Field | Type | Description |
|---|---|---|
private_key |
str |
Base64-encoded private key |
public_key |
str |
Base64-encoded public key |
algorithm |
str |
Algorithm identifier, e.g. "Ed25519" |
issue_credential(...) -> AgentCredential
Issue a signed identity credential for an AI agent.
from cordprotocol import issue_credential, SCOPES
cred = issue_credential(
agent_id="langchain-prod-001", # unique agent identifier
issued_to="acme-corp", # recipient (user, org, etc.)
permissions=[SCOPES.READ, SCOPES.WRITE],
expires_in="7d", # "30m" | "24h" | "7d" | "30d" …
private_key=kp.private_key,
attestation_hash=None, # optional SHA-256 of audit doc
)
| Parameter | Type | Description |
|---|---|---|
agent_id |
str |
Unique identifier for the agent |
issued_to |
str |
Entity receiving the credential |
permissions |
List[str] |
Permission scopes (see SCOPES) |
expires_in |
str |
Duration: m minutes, h hours, d days |
private_key |
str |
Base64-encoded issuer private key |
attestation_hash |
Optional[str] |
SHA-256 hash of an attestation document |
verify_credential(credential, backend=None) -> VerificationResult
Verify a credential's signature and expiry.
from cordprotocol import verify_credential
result = verify_credential(cred)
if result.valid:
print(f"Agent {result.credential.agent_id} verified")
else:
print(f"Rejected: {result.error}")
| Field | Type | Description |
|---|---|---|
valid |
bool |
True if signature and expiry are OK |
error |
Optional[str] |
Reason for failure when valid=False |
credential |
Optional[AgentCredential] |
The credential on success |
is_expired(credential) -> bool
from cordprotocol import is_expired
print(is_expired(cred)) # False (for a freshly issued credential)
has_permission(credential, scope) -> bool
from cordprotocol import has_permission, SCOPES
print(has_permission(cred, SCOPES.WRITE)) # True | False
SCOPES
Standard permission scopes, compatible with the TypeScript SDK:
| Constant | Value | Purpose |
|---|---|---|
SCOPES.READ |
"read:data" |
Read access to data sources |
SCOPES.WRITE |
"write:data" |
Write / mutate data |
SCOPES.EXECUTE |
"execute:actions" |
Trigger external actions |
SCOPES.COMMUNICATE |
"communicate:agents" |
Talk to other agents |
SCOPES.SPEND |
"spend:budget" |
Authorise spending operations |
AgentCredential
Pydantic model — schema identical to the TypeScript AgentCredential.
| Field | Type | Description |
|---|---|---|
id |
str |
UUID v4 |
agent_id |
str |
Agent identifier |
issued_to |
str |
Recipient |
issued_at |
datetime |
UTC issue time |
expires_at |
datetime |
UTC expiry time |
permissions |
List[str] |
Granted scopes |
attestation_hash |
Optional[str] |
Optional audit hash |
issuer_public_key |
str |
Base64 public key |
signature |
str |
Base64 signature |
Serialisation helpers: .to_dict(), .from_dict(), .to_json(), .from_json().
Integration examples
LangChain
from cordprotocol import generate_keypair, issue_credential, verify_credential, SCOPES
ISSUER_KP = generate_keypair() # generate once, store in your KMS
def make_agent_credential(agent_id: str):
return issue_credential(
agent_id=agent_id,
issued_to="langchain-runtime",
permissions=[SCOPES.READ, SCOPES.EXECUTE],
expires_in="1h",
private_key=ISSUER_KP.private_key,
)
class CordProtectedTool(BaseTool): # from langchain.tools
name = "my_tool"
description = "..."
def __init__(self, credential):
super().__init__()
self.credential = credential
def _run(self, query: str) -> str:
result = verify_credential(self.credential)
if not result.valid:
raise PermissionError(f"Identity check failed: {result.error}")
if not has_permission(self.credential, SCOPES.READ):
raise PermissionError("Missing read:data permission")
# ... your tool logic here
See examples/langchain_example.py for the full runnable demo.
CrewAI
from cordprotocol import generate_keypair, issue_credential, SCOPES
registry_kp = generate_keypair()
def register_crew_agent(agent_id: str, permissions: list):
return issue_credential(
agent_id=agent_id,
issued_to="crewai-runtime",
permissions=permissions,
expires_in="2h",
private_key=registry_kp.private_key,
)
See examples/crewai_example.py for the full trust-registry pattern.
CLI
# Generate a keypair
cord keygen
# Issue a credential
cord issue \
--agent-id my-agent \
--issued-to acme \
--permissions read:data,write:data \
--expires-in 24h \
--private-key <base64-private-key>
# Verify a saved credential
cord verify credential.json
DID & Verifiable Credentials (W3C Standard)
cordprotocol v0.3.0 adds full W3C DID and Verifiable Credential support, compatible with the TypeScript SDK v0.4.0.
Issue a Verifiable Credential
from cordprotocol import generate_keypair, issue_verifiable_credential, agent_id_to_did
kp = generate_keypair()
vc = issue_verifiable_credential(
agent_id="trading-agent",
issued_to="paul@example.com",
permissions=["read:market", "execute:trades"],
expires_in="24h",
private_key=kp.private_key,
issuer_did="did:web:cordprotocol.dev",
)
print(vc.id) # "urn:uuid:<uuid>"
print(vc.issuer) # "did:web:cordprotocol.dev"
print(vc.proof.type) # "Ed25519Signature2020"
Verify a Verifiable Credential
from cordprotocol import verify_verifiable_credential
result = verify_verifiable_credential(vc)
# result["valid"] → True
# result["agent_id"] → "trading-agent"
# result["permissions"] → ["read:market", "execute:trades"]
# result["reason"] → None
Verification is fully offline — the issuer's public key is recovered from the did:key embedded in proof.verification_method.
DID utilities
from cordprotocol import agent_id_to_did, did_to_agent_id
did = agent_id_to_did("trading-agent")
# → "did:web:cordprotocol.dev:agents:trading-agent"
agent_id = did_to_agent_id(did)
# → "trading-agent"
Create a DID Document
from cordprotocol import generate_keypair, create_did_document
kp = generate_keypair()
doc = create_did_document(
did="did:web:cordprotocol.dev:agents:my-agent",
public_key=kp.public_key,
service_endpoint="https://my-agent.example.com",
)
print(doc.verification_method[0].type) # "Ed25519VerificationKey2020"
print(doc.verification_method[0].public_key_multibase) # "z<base58btc>"
Resolve a DID
from cordprotocol import resolve_did, resolve_did_sync
# did:key — offline, no network
result = resolve_did_sync("did:key:z6Mk...")
doc = result.did_document
# did:web — fetches https://<domain>/.well-known/did.json or path equivalent
result = await resolve_did("did:web:cordprotocol.dev:agents:trading-agent")
Convert between AgentCredential and VC
from cordprotocol import (
issue_credential, agent_credential_to_vc, vc_to_agent_credential_dict,
generate_keypair, SCOPES,
)
kp = generate_keypair()
cred = issue_credential(
agent_id="my-agent", issued_to="alice",
permissions=[SCOPES.READ], expires_in="24h",
private_key=kp.private_key,
)
# Convert to W3C VC format
vc = agent_credential_to_vc(cred, issuer_did="did:web:cordprotocol.dev")
# Convert back to AgentCredential-compatible dict
d = vc_to_agent_credential_dict(vc)
Key encoding utilities
from cordprotocol import public_key_to_multibase, multibase_to_public_key
multibase = public_key_to_multibase(kp.public_key) # "z<base58btc>"
base64_key = multibase_to_public_key(multibase) # original base64
Post-quantum roadmap
The SDK is designed for a seamless upgrade to CRYSTALS-Dilithium (NIST PQC standard). Every location that needs to change is marked with [PQ SWAP POINT] in the source. The swap requires changing one line:
# cordprotocol/crypto/signatures.py
default_backend: CryptoBackend = DilithiumBackend() # was Ed25519Backend()
No changes are needed in application code.
Hosted API integration
The CordProtocol client wraps the core SDK with optional registry auto-posting and revocation checking against the live API at https://api.cordprotocol.dev.
Basic usage (unchanged)
from cordprotocol import generate_keypair, issue_credential, verify_credential, SCOPES
kp = generate_keypair()
cred = issue_credential(
agent_id="my-agent",
issued_to="paul@example.com",
permissions=["read:data"],
expires_in="24h",
private_key=kp.private_key,
)
result = verify_credential(cred)
assert result.valid
With registry and revocation (new in v0.2.0)
from cordprotocol import CordProtocol, CordProtocolConfig, generate_keypair, SCOPES
kp = generate_keypair()
cord = CordProtocol(CordProtocolConfig(
registry=True,
api_key=os.environ.get("CORD_API_KEY"),
))
# Issues the credential AND automatically registers the public key
# in the Cord Protocol registry. Registry failure is silent.
cred = cord.issue_credential(
agent_id="my-agent",
issued_to="paul@example.com",
permissions=["read:data", "write:orders"],
expires_in="24h",
private_key=kp.private_key,
)
# Verifies signature + expiry AND checks revocation status via the API.
result = cord.verify_credential(cred)
if not result.valid:
print(result.error) # e.g. "Credential has been revoked."
# Revoke a credential (requires api_key)
cord.revoke_credential(cred.id, cred.agent_id, reason="decommissioned")
# Look up a registered agent
registration = cord.lookup_agent("my-agent")
if registration:
print(registration.active, registration.credential_count)
Low-level registry functions
All registry functions are also available directly, as both async and sync variants:
from cordprotocol import (
register_agent, register_agent_sync,
lookup_agent, lookup_agent_sync,
check_revocation_status, check_revocation_status_sync,
revoke_credential, revoke_credential_sync,
)
# Async (use inside async functions)
registration = await register_agent("my-agent", kp.public_key, "paul@example.com")
status = await check_revocation_status(cred.id)
# Sync (use in regular code)
registration = register_agent_sync("my-agent", kp.public_key, "paul@example.com")
status = check_revocation_status_sync(cred.id)
# {"revoked": False, "revoked_at": None, "reason": None}
Error handling
from cordprotocol import RegistryError, RevocationError
try:
await register_agent("my-agent", kp.public_key, "alice")
except RegistryError as e:
print(f"Registry error: {e}")
try:
cord.revoke_credential(cred.id, cred.agent_id)
except RevocationError as e:
print(f"Revocation failed: {e}")
except ValueError as e:
print(f"Config error: {e}") # no api_key set
Links
- Website: cordprotocol.dev
- npm package:
@cordprotocol/sdk - Issues: GitHub Issues
License
MIT
Project details
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 cordprotocol-0.3.1.tar.gz.
File metadata
- Download URL: cordprotocol-0.3.1.tar.gz
- Upload date:
- Size: 43.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c67b5dd2cd3d97135d57e9dd23112274fbe8126651647bb31f1c63e1bb99682
|
|
| MD5 |
3e92061ef12ab5239f1a2b3f6befa296
|
|
| BLAKE2b-256 |
38fcfc119f410fc5617f8c4a44b2d65d8a6cc3c1ea0b0f6c90a8a35e969d1d19
|
File details
Details for the file cordprotocol-0.3.1-py3-none-any.whl.
File metadata
- Download URL: cordprotocol-0.3.1-py3-none-any.whl
- Upload date:
- Size: 28.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
025c697bcfe3e262ee1cbc445f55d9c285ee7a39f8df7ef403fac448060a3844
|
|
| MD5 |
24ef6188eab590076d9c0a84ee56ce75
|
|
| BLAKE2b-256 |
fab07b16699b63accbd6674e9920d145b835459ee44a5447255d1e8f55d123c9
|