Python SDK for AIM (Agent Identity Management) - Automatic identity verification for AI agents
Project description
AIM Python SDK
Cryptographic identity, capability authorization, and audit trails for Python AI agents. Apache 2.0.
Part of Agent Identity Management (AIM). Managed hosting at aim.opena2a.org/get-started; self-host via the main README.
Quick start
from aim_sdk import secure
agent = secure("my-first-agent")
@agent.perform_action(capability="db:read")
def get_customer(customer_id):
return db.query("SELECT * FROM customers WHERE id = ?", customer_id)
secure() generates an Ed25519 keypair, registers the agent with the AIM backend, and stores credentials at ~/.aim/. @perform_action signs every invocation, runs it through 5-step Fine-Grained Authorization on the server, and records the outcome in the audit log.
Install:
pip install aim-sdk
aim-sdk login # OAuth to aim.opena2a.org
aim-sdk login --url http://localhost:8080 # or to your self-hosted AIM
Login uses OAuth 2.0 with PKCE. Credentials save to ~/.aim/sdk_credentials.json (mode 0600).
Framework auto-detection
secure() reads sys.modules to detect the agent's framework or LLM provider from imports:
| Category | Detected from | Mapped agent_type |
|---|---|---|
| Frameworks | langchain, crewai, autogen, llama_index, haystack, semantic_kernel, langgraph |
langchain, crewai, etc. |
| LLM providers | anthropic, openai, google.generativeai, mistralai, cohere |
claude, gpt, gemini, mistral, cohere |
Frameworks take priority over LLM providers. If both langchain and anthropic are imported, the agent type is langchain.
Override explicitly:
from aim_sdk import secure, AgentType
agent = secure("my-agent", agent_type=AgentType.CREWAI)
Auto-instrumentation
After secure() registers the agent, the SDK installs no-op-on-failure hooks for any of these libraries that are present:
- LangChain
- CrewAI
- OpenAI
- Anthropic
Each model call (chat completion, embedding, tool call) is recorded to the audit trail. The hooks never raise — a hook failure logs a warning and the call proceeds.
Disable: secure("my-agent", auto_hooks=False).
Capability decorators
@agent.perform_action signs each invocation, runs it through FGA on the server, and records the outcome. Risk level auto-detects from the capability string using two lookup tables in aim_sdk/risk_detector.py:
- Namespace prefix maps
payment:,admin:,system:,billing:,finance:to critical;email:,notification:,sms:,user:,auth:,secret:,credential:to high;db:,database:,file:,storage:,cache:to medium;api:,weather:,search:,geocode:,translate:,time:,math:,util:to low. - Action suffix maps
:read,:fetch,:get,:list,:query,:view,:check,:validateto low;:write,:update,:create,:modify,:save,:uploadto medium;:delete,:send,:execute,:run,:invoke,:export,:transferto high;:process,:refund,:charge,:approve,:drop,:truncate,:wipe,:terminateto critical.
When namespace and action disagree the higher risk wins. SPECIFIC_CAPABILITY_MAP overrides both for known patterns (for example user:delete escalates to critical).
@agent.perform_action(capability="db:read") # medium (db: medium, :read low → max = medium)
def get_customer(customer_id): ...
@agent.perform_action(capability="db:delete") # high (SPECIFIC_CAPABILITY_MAP override)
def delete_customer(customer_id): ...
@agent.perform_action(capability="payment:refund",
risk_level="critical",
jit_access=True,
timeout_seconds=300)
def process_refund(order_id, amount): ... # waits for admin approval
JIT access
jit_access=True pauses execution and creates an approval request in the AIM dashboard. The function returns only after a human approves, or raises JITAccessTimeoutError after timeout_seconds.
Enforcement mode
The organization's enforcement mode (configured in dashboard Settings → Security → Policies) controls what happens on verification failure:
- Monitoring (default) — warning logged, function executes anyway. For dev and gradual rollout.
- Strict —
PermissionErrorraised, function blocked. For production and compliance.
Override locally for testing: AIM_STRICT_MODE=true python my_agent.py. Production deployments configure in the dashboard, not via env var.
Capability declaration
The SDK supports three ways to declare capabilities. Decorators are preferred.
- Decorators —
@agent.perform_action(capability="...")in code. Most accurate. - Config file —
~/.aim/capabilities.jsonwith{"capabilities": ["db:read", ...]}. For static declarations. - Explicit at registration —
secure("my-agent", capabilities=["api:call", "db:read"]).
Auto-detection from sys.modules is disabled by default — it's noisy and misleading (almost every Python agent imports the same generic packages).
Request additional capabilities
After registration, new capabilities require admin approval (prevents privilege escalation per CVE-2025-32711):
result = agent.request_capability(
capability_type="db:write",
reason="Need to update user preferences"
)
if result["status"] == "pending":
print(f"Request {result['id']} submitted - awaiting admin approval")
elif result["status"] == "approved":
print("Capability granted")
MCP server registration
secure() auto-discovers MCP servers via Claude Desktop config and queries each server using the MCP protocol (tools/list):
agent = secure("my-agent", mcp_servers=["filesystem", "github"])
# SDK queries each server, discovers actual capabilities, auto-attests.
Manual registration:
agent.register_mcp(
server_name="my-database-server",
server_url="http://localhost:3001",
capabilities=["db:read", "db:write", "data:delete"]
)
Discover capabilities without attesting:
from aim_sdk.detection import discover_mcp_capabilities
caps = discover_mcp_capabilities(["filesystem", "github"])
# {"filesystem": ["read_file", "write_file", ...], "github": [...]}
Credential storage
| Path | Contents | Mode |
|---|---|---|
~/.aim/sdk_credentials.json |
OAuth tokens (from aim-sdk login) |
0600 |
~/.aim/agents/<name>.json |
Per-agent Ed25519 keypair + metadata | 0600 |
~/.aim/credentials.json |
Legacy combined-store; auto-migrated | 0600 |
~/.aim/capabilities.json |
Explicit capability declarations (optional) | 0644 |
The private key is returned once at registration. The SDK saves it locally. Losing the private key means rotating credentials via the dashboard.
To force a fresh registration that bypasses the local cache and reconnects to the backend:
agent = secure("my-agent", force_new=True)
force_new=True is for credential rotation, debugging, or post-database-reset recovery. To create an entirely new agent, use a different name.
CLI commands
The SDK ships with a small CLI for authentication and status:
aim-sdk login # OAuth to AIM Cloud
aim-sdk login --url <URL> # OAuth to self-hosted instance
aim-sdk logout # Clear ~/.aim/sdk_credentials.json
aim-sdk status # Show authentication state
aim-sdk --version # Show SDK version
For SecOps workflows (scanning a codebase, hardening configs, monitoring runtime), see the separate opena2a CLI.
Causal-denial telemetry (opt-in)
When a verification is denied, the SDK can join the injection cause, the classified intent, and the authorization outcome into one local correlated record so you can see why an action was blocked. It is OFF by default and best-effort -- it never changes a verdict and never adds latency to the enforcement path. Two independent opt-ins gate it:
from aim_sdk import AIMClient
from aim_sdk.telemetry import IntentInput, DetectionInput
client = AIMClient(
agent_id="...", api_key="aim_abc123", aim_url="https://aim.opena2a.org",
telemetry={
"enabled": True, # stage 1: capture records locally
"relay": { # stage 2: share anonymized indicators
"enabled": True, # (separate, explicit opt-in)
"package_name": "my-service", # self-declared sensor label
},
},
)
# The injection detector / intent classifier populate the optional seam:
client.verify_capability(
"net:connect", resource="https://example/data",
telemetry={
"intent": IntentInput(intent_class="exfiltration", confidence=0.7,
blocked=True, source="nanomind-intent"),
"detection": DetectionInput(injection_detected=True, confidence=0.84,
detector="nanomind-guard",
technique_source="interim-mapping",
technique_id="T-2002"),
},
)
client.close() # stops the managed joiner/relay threads (or use a `with` block)
Two tiers, by design. The full correlated record is authoritative and stays
on the machine (~/.opena2a/correlated-events.jsonl). Only an anonymized
indicator -- event type, Threat-Matrix technique ID, confidence, runtime,
and an anonymous per-device sensor token -- may leave, and only when relay
sharing is opted in. Identifiers (agent ID, resource, capability, credential
references, payloads, the correlation key) are never shared, and the relay
egress-validates technique fields before transmission. Only
denied_injection_attempt indicators are uploaded, to the Registry's public,
count-only endpoint.
Manual mode (no OAuth)
For CI environments or pre-configured credentials, skip aim-sdk login and pass an API key:
agent = secure("my-agent", api_key="aim_abc123")
Or supply full credentials:
from aim_sdk import AIMClient
client = AIMClient(
agent_id="550e8400-e29b-41d4-a716-446655440000",
public_key="<base64-Ed25519>",
private_key="<base64-Ed25519>",
aim_url="https://aim.opena2a.org"
)
@client.perform_action(capability="db:read")
def get_customer(customer_id): ...
Examples
Working examples in examples/:
| Example | Shows |
|---|---|
example.py |
Decorator-based verification, manual mode |
example_auto_detection.py |
Framework + MCP auto-discovery (no backend required) |
example_one_line_setup.py |
Zero-config secure() flow (requires backend) |
Framework integration guides:
Requirements
- Python 3.8+
requests(HTTP)pynacl(Ed25519)cryptography(TLS, secure storage)keyring(OS keychain for OAuth tokens)
All install via pip install aim-sdk.
Versioning
Semantic Versioning 2.0.0. Current: see VERSION file. SDK 1.x.x is compatible with backend 1.x.x; SDK 2.x.x requires backend 2.x.x.
import aim_sdk
print(aim_sdk.__version__)
See CHANGELOG.md for history, docs/VERSIONING.md for the support policy.
Related
- Java SDK — same API shape, AspectJ-based decoration
- TypeScript SDK — local-or-server mode
- opena2a CLI — codebase auditing, credential migration, runtime monitoring
- AIM backend — server, dashboard, deployment
License
Apache-2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
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 aim_sdk-1.24.1.tar.gz.
File metadata
- Download URL: aim_sdk-1.24.1.tar.gz
- Upload date:
- Size: 254.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01b3001d6a07b20a9c118c116f0d5fe83443ec1030a9341d6c55e8b0ff21e8f3
|
|
| MD5 |
5616c812300d61e338a8bb7f83a9784e
|
|
| BLAKE2b-256 |
9d976301c8b3f6bfbe482e43394c3df0c2ea7a35c1e9c058bb7ffe0734e8b515
|
Provenance
The following attestation bundles were made for aim_sdk-1.24.1.tar.gz:
Publisher:
release.yml on opena2a-org/agent-identity-management
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aim_sdk-1.24.1.tar.gz -
Subject digest:
01b3001d6a07b20a9c118c116f0d5fe83443ec1030a9341d6c55e8b0ff21e8f3 - Sigstore transparency entry: 1755662988
- Sigstore integration time:
-
Permalink:
opena2a-org/agent-identity-management@e9f29e04fbc9062e35be20442d00dcf9b088c21d -
Branch / Tag:
refs/tags/sdk-py-v1.24.1 - Owner: https://github.com/opena2a-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e9f29e04fbc9062e35be20442d00dcf9b088c21d -
Trigger Event:
push
-
Statement type:
File details
Details for the file aim_sdk-1.24.1-py3-none-any.whl.
File metadata
- Download URL: aim_sdk-1.24.1-py3-none-any.whl
- Upload date:
- Size: 195.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
871723ba2187983ade9d131e7bbca6c8d8271f2d3b848b864dafbf28c330aa57
|
|
| MD5 |
e6c2fb4c89f68cadd0030a59721e6341
|
|
| BLAKE2b-256 |
134d4d4b054b9bc880721c7e2c015d0e64152e1685adc6dc2a8a19f08aad3f1c
|
Provenance
The following attestation bundles were made for aim_sdk-1.24.1-py3-none-any.whl:
Publisher:
release.yml on opena2a-org/agent-identity-management
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aim_sdk-1.24.1-py3-none-any.whl -
Subject digest:
871723ba2187983ade9d131e7bbca6c8d8271f2d3b848b864dafbf28c330aa57 - Sigstore transparency entry: 1755663016
- Sigstore integration time:
-
Permalink:
opena2a-org/agent-identity-management@e9f29e04fbc9062e35be20442d00dcf9b088c21d -
Branch / Tag:
refs/tags/sdk-py-v1.24.1 - Owner: https://github.com/opena2a-org
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e9f29e04fbc9062e35be20442d00dcf9b088c21d -
Trigger Event:
push
-
Statement type: