vorim
Official Python SDK for Vorim AI — the identity, permissions, and audit layer for AI agents.
Vorim AI provides cryptographic agent identities (Ed25519), fine-grained permissions (7 scopes), immutable audit trails, and trust scoring (0-100) for production AI agent deployments. Built for the traceability and audit-trail obligations of frameworks like the EU AI Act.
API key: create one in the Vorim dashboard under Settings, API keys. No account yet? Request access at vorim.ai. Documentation — Full API reference, framework integrations, and examples. Quick Start — Set up in under 5 minutes.
Install
Requires Python 3.10+.
pip install vorim
With framework integrations (keep the quotes, zsh treats unquoted square brackets as a glob):
pip install "vorim[langchain]" # LangChain / LangGraph
pip install "vorim[crewai]" # CrewAI
pip install "vorim[openai]" # OpenAI Agents SDK
pip install "vorim[all]" # All integrations
Quick Start
The key for this quick start needs the agents:read, agents:write, permissions:read, audit:read and audit:write scopes. Scopes are independent, so agents:write does not include agents:read.
from vorim import Vorim
vorim = Vorim(api_key="agid_sk_live_...")
# Register an agent (returns Ed25519 keypair, private key shown once)
result = vorim.register(
name="invoice-processor",
capabilities=["read_documents", "extract_data"],
scopes=["agent:read", "agent:execute"],
)
print(result.agent.agent_id) # agid_acme_a1b2c3d4
print(result.agent.trust_score) # 50
# Check permissions (<5ms via Redis)
check = vorim.check(result.agent.agent_id, "agent:execute")
if check.allowed:
# Emit audit event
vorim.emit(
agent_id=result.agent.agent_id,
event_type="tool_call",
action="process_invoice",
resource="INV-2026-0042",
result="success",
latency_ms=142,
)
# Verify any agent's trust (public, no auth required)
trust = vorim.verify(result.agent.agent_id)
print(f"Trust score: {trust.trust_score}/100")
Async Client
import asyncio
from vorim import AsyncVorim
async def main():
async with AsyncVorim(api_key="agid_sk_live_...") as vorim:
result = await vorim.register(
name="my-agent",
capabilities=["search"],
scopes=["agent:read"],
)
trust = await vorim.verify(result.agent.agent_id)
print(f"Trust score: {trust.trust_score}/100")
asyncio.run(main())
API Reference
Vorim(api_key, base_url?, timeout?, auto_sign=True)
| Method | Description |
|---|---|
register(name, capabilities, scopes) |
Register an agent; caches the returned Ed25519 private key for auto-signing |
check(agent_id, scope) |
Check if agent has permission (sub-5ms) |
emit(agent_id, event_type, action, ..., sign=None) |
Emit an audit event. Auto-signed if the agent's key is in the keyring |
emit_batch(events, sign=None) |
Emit up to 1,000 audit events. Auto-signs each one |
verify(agent_id) |
Verify agent identity and trust score (public) |
get_agent(agent_id) |
Get agent details |
list_agents(status?, page?, per_page?) |
List agents with filtering |
revoke(agent_id) |
Permanently revoke an agent |
grant(agent_id, scope, valid_until?, rate_limit?) |
Grant a permission scope |
use_agent_key(agent_id, private_key_pem) |
Restore a private key into the in-memory keyring after process restart |
forget_agent_key(agent_id) |
Remove a private key from the keyring |
Module-level helpers: canonical_payload_v1(event) and canonical_payload_v0(event) (return the bytes that get signed) and sign_payload(payload, private_key_pem) (returns ed25519:<base64>).
AsyncVorim has the same interface with await on all methods.
Per-event signing (auto-signing)
From v3.7.0, every audit event is signed at source with the agent's Ed25519 private key — no code change required. register() caches the returned key in memory and emit() attaches the signature transparently.
Canonical form. Since 3.7.0 the SDK defaults to v1 canonical form: RFC 8785 JSON Canonicalization Scheme (JCS) over the whole event minus signature and canonical_form. v1 brings metadata, replayable-evidence fields (model_version, tool_catalogue_hash, system_prompt_hash, prev_event_hash), and delegation context (on_behalf_of, delegator_agent_id, delegation_chain_id, delegation_depth) under the signature. The previous v0 form was a pipe-joined six-field string event_type|action|resource|input_hash|output_hash|result and is now deprecated — passing canonical_form="v0" explicitly still works for verifier-compat scenarios but logs a deprecation warning. Use @vorim/verify@0.2.0+ (or the v1 helpers in this package) to verify v1 events offline.
result = vorim.register(name="agent", capabilities=[], scopes=["agent:execute"])
# Auto-signed. The signature is attached before the request leaves the process.
vorim.emit(
agent_id=result.agent.agent_id,
event_type="tool_call",
action="transfer_funds",
result="success",
)
To verify signatures server-side, the API operator sets VORIM_VERIFY_AUDIT_SIGNATURES=true.
Restoring keys across process restarts. The in-memory keyring is lost on restart. Load the private key from your secret store:
vorim.use_agent_key(agent_id, private_key_pem)
vorim.forget_agent_key(agent_id) # revoke from memory
Opting out. Per event with sign=False, or globally with auto_sign=False:
vorim.emit(agent_id=..., event_type=..., action=..., result=..., sign=False)
vorim = Vorim(api_key=..., auto_sign=False)
Runtime Control (gate actions before they happen)
Ask Vorim whether an action should proceed before your agent performs it.
before_action() returns a typed decision and, by default, raises on deny
(a denial is in the response body, not the HTTP status).
from vorim import Vorim, VorimDeniedError
vorim = Vorim(api_key="agid_sk_live_...")
try:
decision = vorim.before_action(
agent_id="agid_acme_a1b2c3d4", # always the public agid_* id
action_type="tool_call",
action_target="sendEmail",
required_scope="agent:communicate",
payload={"to": "customer@example.com", "body": "..."},
)
payload = decision.modified_payload or {"to": "customer@example.com"}
if decision.decision in ("allow", "modify"):
send_email(payload)
elif decision.decision == "escalate":
resolved = vorim.wait_for_decision_resolution(decision.decision_id)
if resolved.decision == "allow":
send_email(payload)
# Link the post-action audit event back to the decision.
vorim.emit(
agent_id="agid_acme_a1b2c3d4",
event_type="tool_call",
action="sendEmail",
result="success",
decision_id=decision.decision_id, # correlates audit ↔ decision
)
except VorimDeniedError as err:
print("Action denied:", err.decision.reason)
Verdicts: allow · deny (raises VorimDeniedError by default) ·
modify (use decision.modified_payload) · escalate (poll
wait_for_decision_resolution) · fallback.
modifyis client-cooperative. Vorim returns the sanitisedmodified_payload; your agent must send it in place of the original. The platform does not sit inline and does not currently enforce that you do — carrydecision_idinto the matchingemit()so the action stays auditable.
Fail-open: if the decision API is unreachable, before_action() returns a
synthetic fallback decision. Pass runtime_fail_open=False to the constructor
to fail closed. A reachable server returning deny always denies. AsyncVorim
exposes the same async methods.
Requires an API key with the
runtime:decidescope and a Growth+ plan.modifyverdicts come from policy rules provisioned by Vorim (rule-authoring API ships in a later release).
Permission Scopes
| Scope | Description |
|---|---|
agent:read |
Read data on behalf of owner |
agent:write |
Write or modify data |
agent:execute |
Trigger actions or tool calls |
agent:transact |
Financial or contractual actions |
agent:communicate |
Send messages or emails |
agent:delegate |
Sub-delegate to other agents |
agent:elevate |
Request permission elevation |
Payload Privacy
Your prompts and model outputs never have to reach Vorim. The audit event carries a digest of them, computed in your process.
A bare SHA-256 of a prompt is not a commitment, it is a lookup key: prompts are low-entropy and usually templated, so anyone holding the digest and the template recovers the content by trying candidates. Use a keyed commitment for anything a human or a model wrote.
from vorim import Vorim, commit_payload
key = os.environ["VORIM_COMMITMENT_KEY"] # never sent to Vorim
v.emit(
agent_id=agent.agent_id,
event_type="tool_call",
action="invoice.refund",
result="success",
input_hash=commit_payload(prompt, key), # hmac-sha256:...
output_hash=commit_payload(completion, key),
)
Keep hash_payload for content that is already high-entropy. To disclose later,
hand the verifier the content and the key; they recompute and compare.
Refuse to send content at all:
v = Vorim(api_key=..., no_payload=True) # no_payload_max_metadata_chars=256
v.emit(..., metadata={"prompt": full_prompt}) # raises VorimError PAYLOAD_BLOCKED
Not the same as the server-side Zero Data Retention setting, which drops metadata when we persist it, after the content has crossed the network. This means it never left your process.
Every emit returns a stable content digest beside the event id, and
event_digest(event) recomputes it locally. Byte-identical to @vorim/sdk and
@vorim/verify.
Framework Integrations
LangChain / LangGraph
from vorim import Vorim
from vorim.integrations.langchain import vorim_tool, VorimCallbackHandler
vorim = Vorim(api_key="agid_sk_live_...")
@vorim_tool(vorim, agent_id="agid_acme_...", permission="agent:execute")
def search(query: str) -> str:
"""Search documents."""
return f"Results for {query}"
# search() is now a standard LangChain tool with built-in permission checks + audit
CrewAI
from vorim import Vorim
from vorim.integrations.crewai import register_crew
vorim = Vorim(api_key="agid_sk_live_...")
crew = register_crew(vorim, {
"crew_name": "content-pipeline",
"members": [
{
"role": "researcher",
"name": "crew-researcher",
"capabilities": ["web_search"],
"scopes": ["agent:read", "agent:execute"],
},
],
})
OpenAI Function Calling
from openai import OpenAI
from vorim import Vorim
from vorim.integrations.openai_agents import VorimToolRegistry
vorim = Vorim(api_key="agid_sk_live_...")
client = OpenAI()
registry = VorimToolRegistry(vorim=vorim, agent_id="agid_acme_...")
registry.add(
name="search",
description="Search documents",
parameters={"type": "object", "properties": {"query": {"type": "string"}}},
execute=lambda args: f"Results for {args['query']}",
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Search for AI papers"}],
tools=registry.to_openai_tools(),
)
# Permission checked + audited automatically
tool_messages = registry.execute_tool_calls(
response.choices[0].message.tool_calls or []
)
Resources
License
MIT
Release files for vorim 3.22.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vorim-3.22.1.tar.gz | 481.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vorim-3.22.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 689.7 kB
Release files / vorim-3.22.1.tar.gz
| Download URL | vorim-3.22.1.tar.gz |
|---|---|
| Size | 481.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
85ec0a8c96aa544eb390a07e3ef800e10b1cad1893f604afa609e935c198a32b
|
|
BLAKE2b-256 checksum How to use checksums |
89ba054b65c5e1319095f49707484d14b790d4e3e2af7f766d19354b4a581f62
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.8
|
Release files / vorim-3.22.1-py3-none-any.whl
| Download URL | vorim-3.22.1-py3-none-any.whl |
|---|---|
| Size | 208.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
60f0fe32c6cbf2290901c03e1d459aab61a8b810f3a9021de7012c02f154cc6d
|
|
BLAKE2b-256 checksum How to use checksums |
a18f428d68f7f1a034dfb5b5cac61ef0640da6bfb3b1e995146b63f1c9789f59
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.8
|