OpenA2A: CLI · HackMyAgent · Secretless · AIM · Browser Guard · DVAA
Cryptographic identity, capability authorization, and audit trails for AI agents. Apache 2.0.
Install the SDK and authenticate:
pip install aim-sdk
aim-sdk login # OAuth to aim.opena2a.orgSelf-hosted: create the agent under Agents in your dashboard first and run the SDK with the credentials it issues; aim-sdk login --url and secure(..., api_key=...) do not yet complete against a self-hosted backend (measured 2026-09-22 on the published images; tracked for a fix).
Then protect any function with a capability grant:
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, and records the outcome in the audit log.
A new organization starts in monitoring mode: AIM grants a capability the moment the agent registers it and records the call, so on those defaults the db:write call below runs. The refusal below needs strict mode, one setting an administrator turns on per organization: in the dashboard, Security, then Policies, then set Global Enforcement Mode to Strict (the API is PUT /api/v1/admin/enforcement-settings with {"enforcementMode": "strict"}). Check it before the call: the Global Enforcement Mode badge reads STRICT, and GET /api/v1/admin/enforcement-settings with an administrator's token returns "enforcementMode": "strict".
Now call something the agent was not granted. my-first-agent holds db:read only:
@agent.perform_action(capability="db:write")
def delete_customer(customer_id):
return db.execute("DELETE FROM customers WHERE id = ?", customer_id)
delete_customer(42)AIM refuses it before the function body runs, and says why:
Capability 'db:write' pending admin approval (strict mode)
✓ Registered db:write (medium)
aim_sdk.exceptions.ActionDeniedError: AIM denied 'db:write': Capability violation blocked by security policy 'Capability Violation Detection': Agent does not have permission for capability 'db:write' (allowed: [db:read]). The action was blocked and not executed. AIM denies an action after applying your organization's enforcement mode (currently strict), so a denial blocks in every mode -- monitoring mode governs verifications AIM could not answer, not ones it refused. To permit this capability, grant it to this agent in the AIM dashboard under Agents. If the agent should not be denied at all, check its status there: an agent marked compromised, suspended or unverified is refused regardless of its capabilities.
Output copied from aim-sdk 2.0.3 against a self-hosted stack on 2026-09-22 (server commit ce68f10), with the organization in strict mode: the decorator files a capability request for db:write, the server refuses the call, and the audit log for the agent records the denial with the same reason. Two things a new agent meets first, also measured on that stack: an agent starts pending and every call is refused with Agent not verified - all actions denied until an administrator verifies it under Agents in the dashboard, and an ActionDeniedError is a PermissionError, so an existing except PermissionError handler catches it. Verify the agent before its first call: on a strict-mode organization, calls refused while pending are recorded against the agent's trust score, and a denied call after that can suspend it (measured 2026-09-23; an agent verified first is not affected).
New to AIM? The SDK quickstart tutorial walks through this end to end. The same one-line shape works in Java and TypeScript.
Auditing an existing codebase instead of integrating? The opena2a CLI provides a 6-phase review with no server required.
The two calls above are the whole model: every decorated call is checked against the agent's grant and logged; in strict mode a capability the agent was not granted is refused before the function body executes, with the reason. Nothing in your code changes between the two, only the grant.
The agent's audit log in the dashboard records each decision: the denied db:write above appears with denialReason set to the same text, the capability, the risk level and the time (that row was read back through the API on the measured run). Open http://localhost:3000 (self-hosted) or the AIM Cloud dashboard, then Agents, then your agent, then its activity. To permit db:write, grant it to the agent there.
Want to see the same boundary stop a real attack? The Demos below end with an intentionally vulnerable agent run with and without AIM.
| Mode | When | Includes |
|---|---|---|
| AIM Cloud | Managed, fastest path | Production-managed at aim.opena2a.org/get-started. Python and Java SDKs work out of the box. |
| Self-hosted | Team or fleet, your infrastructure | All AIM Cloud features. PostgreSQL audit, REST API, dashboard, OAuth, 5-step FGA, 9-factor real-time trust, MCP attestation, PAM, SIEM adapters. |
| Local-only | Solo developer, single machine, no server | TypeScript SDK + opena2a CLI. Ed25519 keypair, audit.jsonl, YAML capability policies, 8-factor local trust score, cross-tool event bridges. Python and Java local mode is on the roadmap. |
All three share the same audit-event schema. Local agents can push history to a server via AIMCore.enableReporting().
| SDK | Install | Mode | API |
|---|---|---|---|
| Python | pip install aim-sdk |
Server (today) | secure("name") + @perform_action |
| Java | cd sdk/java && mvn install (from source) |
Server | AIMClient.secure("name") + @SecureAction |
| TypeScript | npm install @opena2a/aim-core |
Local or server | new AIMClient({ agentId }) |
Working examples for all three live in examples/.
secure() auto-detects your framework from imports (langchain, crewai, llama_index, anthropic, openai). When both a framework and an LLM provider are present, the framework wins.
@agent.perform_action signs each invocation, runs it through 5-step FGA on the server, and records the outcome. Risk level auto-detects from the capability string using two lookup tables in sdk/python/aim_sdk/risk_detector.py:
- Namespace prefix.
payment:,admin:,system:,billing:,finance:map to critical.email:,notification:,sms:,user:,auth:,secret:,credential:map to high.db:,database:,file:,storage:,cache:map to medium.api:,weather:,search:,geocode:,translate:,time:,math:,util:map to low. - Action suffix.
:read,:fetch,:get,:list,:query,:view,:check,:validatemap to low.:write,:update,:create,:modify,:save,:uploadmap to medium.:delete,:send,:execute,:run,:invoke,:export,:transfermap to high.:process,:refund,:charge,:approve,:drop,:truncate,:wipe,:terminatemap to critical.
When namespace and action disagree the higher risk wins. A SPECIFIC_CAPABILITY_MAP in the same file overrides both for known patterns (for example user:delete escalates to critical). Pass risk_level="critical" to override, and jit_access=True to pause execution until a human approves in the dashboard.
Full example: examples/flight-search-agent/flight_agent.py.
AIMClient agent = AIMClient.secure("my-first-agent");
@SecureAction(capability = "db:read", resource = "users_table")
public User getCustomer(String customerId) {
return userRepository.findById(customerId);
}Version 1.0.0, built from source. Same Ed25519 signing, same FGA flow, same audit trail. AspectJ wraps @SecureAction invocations. See sdk/java/README.md.
import { AIMClient } from "@opena2a/aim-core";
const agent = new AIMClient({ agentId: "my-first-agent" });
await agent.verify({ capability: "db:read", resource: "users_table" });The only SDK that runs without a server today. Backs local mode. See sdk/typescript/README.md.
Every privileged action runs through five checks before execution.
| Step | Check | Latency budget |
|---|---|---|
| 1 | Capability | <10ms |
| 2 | Attribute | <10ms |
| 3 | Context | <10ms |
| 4 | Chain | <10ms |
| 5 | Intent (NanoMind) | up to 800ms on HIGH-risk operations |
Step 5 uses the NanoMind security classifier, a 3M-parameter local Mamba model. No external calls.
9-factor real-time trust scoring runs on every action. Per-capability thresholds gate access.
Multi-agent consensus. 3+ attesters across 2+ owners equals verified.
Three tiers: STANDARD, PRIVILEGED, SUPER_PRIVILEGED. Human approval gates, break-glass override, and certification campaigns.
CCP for vaulted credential retrieval. PSM for privileged session recording.
Splunk HEC and Microsoft Sentinel Data Collector. Buffered batch delivery, retry, severity filtering.
Available in Self-hosted and AIM Cloud modes.
Fleet overview: agents monitored, actions blocked, and risk by category.
Agent registry with trust scores and verification status per agent.
Per-agent 9-factor trust score breakdown with weighted signal contributions.
MCP server dependencies with multi-agent attestation status.
The opena2a CLI is a separate tool for SecOps workflows: auditing a codebase, hardening configs on disk, monitoring runtime events. Not required to integrate the SDK.
opena2a review # 6-phase audit of a codebase
opena2a protect # migrate hardcoded credentials → env vars
opena2a guard sign # filesystem integrity signing
opena2a runtime tail # ARP event stream
opena2a identity audit # cross-tool audit log
opena2a identity attach --all # install cross-tool event bridgesInstall:
brew install opena2a-org/tap/opena2a # or
npm install -g opena2a-cliopena2a identity attach --all installs bridges that read other OpenA2A tools' event logs and re-emit each event into one unified JSONL:
Secretless events ──┐
HMA scan findings ──┤
HMA ARP runtime ──┼─→ ~/.opena2a/aim-core/audit.jsonl
ConfigGuard events ──┤
Shield events ──┘
No decorator. No library import in agent code. Run attach --all once, work normally, and after an incident the audit log holds a deduplicated, timestamp-ordered trail of credential injections, file accesses, network calls, config tampering, and scan findings.
Capability authorization (deny-before-execute, FGA, intent classification) requires the server. See Server features.
The
docker pullcommand shown in the box on theaim-dashboardandaim-serverpackage pages names asha256-<hex>tag that holds the build's SLSA provenance attestation, not the image; with Docker 29.2 that pull fails withunsupported media type application/vnd.oci.empty.v1+jsonand installs nothing (measured 2026-09-24). Install through the Docker steps below; they pin and verify nothing.
The quickstart script is fetched from main. On a first run it downloads docker-compose.quickstart.yml, also from main, and starts aim-server and aim-dashboard at their latest tags. latest moves only when a platform release is published; merges to main do not move it. Until the first release after this change, latest names the last build of main published before this change landed, whose version label reads edge. PostgreSQL (the TimescaleDB image) and Redis start at floating upstream tags. Nothing on this path is pinned or verified: read the script and the compose file it fetches before running it. Published security advisories are on the repository's security advisories page.
curl -sSLO https://raw.githubusercontent.com/opena2a-org/agent-identity-management/main/scripts/quickstart.sh
bash quickstart.shBrings up aim-server, aim-dashboard, PostgreSQL, and Redis. Dashboard at localhost:3000, API at localhost:8080. Login credentials print at the end of the run.
Production deployment (Azure, GCP, AWS): infrastructure/DEPLOYMENT.md.
Merges to main no longer publish images: only a push of a platform-vX.Y.Z tag does, and it publishes X.Y.Z, X.Y, X and latest for both images on both registries. The edge tag is frozen at the last build of main published before this change and will be removed; to follow main, build from source (next section). This change does not move 1, 1.0 or 1.0.0. A running image is identified by its digest and its revision label (org.opencontainers.image.revision), not by the tag it was pulled by.
New accounts wait for an administrator's approval. To bootstrap the first administrator, set AIM_PLATFORM_ADMINS (comma-separated emails) before starting the backend: accounts on that list are approved automatically and approve everyone else from the dashboard's admin area. Until a listed address has registered, or some administrator exists, other sign-ups are refused with an error that says so; nothing is queued. The backend logs how the variable was read at startup, so a mistyped entry is visible there.
The aim-server and aim-dashboard image tags 1.23, 1.23.0, 1.5 and 1.5.0, and the aim-server tags 1.22 and 1.22.0, are withdrawn: their numbers sort above 1.0.0, but no platform release has used them. If a pull of one of them fails with manifest unknown, it is because the tag was removed from the registry. The images behind them bundle components with critical-rated vulnerabilities, described in GHSA-rqgr-f9m6-xphr for the dashboard and GHSA-fxfm-hhhw-3mgf for the server. The dashboard images are also affected by GHSA-638x-gwq7-xfh7, rated high. Each of these advisories lists the images it affects and shows how to check a deployment.
Prerequisites: Docker, Go 1.22+, Node 20+, Python 3.11+.
git clone https://github.com/opena2a-org/agent-identity-management.git
cd agent-identity-management
# Generate local-dev secrets (one-time setup)
./scripts/gen-dev-secrets.sh > .env
# Minimal dev stack
docker compose up -d aim-postgres aim-redis aim-backend aim-frontend
# Health check
curl -fsS localhost:8080/health
# Python SDK editable for examples
pip install -e sdk/python
# Try the flight-search-agent demo
cd examples/flight-search-agent && python3 flight_agent.pyThe full docker-compose.yml also brings up Elasticsearch, MinIO, NATS, Prometheus, Grafana, and Loki. Skip those services with the minimal command above.
Version 0.2.0 was built by the release.yml workflow in opena2a-org/opena2a at tag aim-core-v0.2.0 and published through npm Trusted Publishing with SLSA v1 provenance; versions 0.1.0 to 0.1.2 carry no provenance. To check the registry's 0.2.0 tarball against that provenance, run these in an empty directory (needs cosign, jq and openssl):
npm pack @opena2a/aim-core@0.2.0
curl -s https://registry.npmjs.org/-/npm/v1/attestations/@opena2a%2faim-core@0.2.0 \
| jq '.attestations[] | select(.predicateType=="https://slsa.dev/provenance/v1") | .bundle' > aim-core.sigstore.json
cosign verify-blob-attestation --bundle aim-core.sigstore.json --new-bundle-format --type slsaprovenance1 \
--certificate-identity https://github.com/opena2a-org/opena2a/.github/workflows/release.yml@refs/tags/aim-core-v0.2.0 \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--digestAlg sha512 --digest "$(openssl dgst -sha512 -r opena2a-aim-core-0.2.0.tgz | cut -d' ' -f1)"
# Prints "Verified OK" (cosign v3.0.4, 2026-09-24); a changed tarball byte or a different signer exits non-zero.Identity files (~/.opena2a/aim-core/identity.json) are written mode 0600. OAuth tokens live in the OS keychain by default. ~/.opena2a/auth.json stores metadata only.
The local and server trust scores measure different things.
Local (8 factors) answers "is the agent's security posture set up correctly?" Computed from local files. Source: packages/aim-core/src/trust.ts.
| Factor | Weight | Signal |
|---|---|---|
| Identity | 20% | identity.json exists |
| Capabilities | 15% | policy.yaml exists |
| Audit log | 10% | audit.jsonl exists |
| Secrets managed | 15% | Secretless integration active |
| Config signed | 10% | ConfigGuard signatures present |
| Skills verified | 10% | HMA verification on skills |
| Network controlled | 10% | Egress policy detected |
| Heartbeat monitored | 10% | ARP runtime heartbeat present |
Server (9 factors) answers "is the agent behaving in a way that should still be trusted right now?" Updates on every action. Source: apps/backend/internal/domain/trust_score.go.
| Factor | Weight | Source |
|---|---|---|
| Verification status | 25% | Externally attested |
| Uptime | 15% | Observed |
| Action success rate | 15% | Observed |
| Security alerts | 15% | NanoMind-modulated |
| Compliance | 10% | Externally attested |
| Execution isolation | 10% | Externally attested |
| Agent age | 5% | Server-recorded |
| Drift detection | 3% | Observed |
| User feedback | 2% | Human input |
Both can run for the same agent when local-to-server reporting is enabled.
The backend emits OpenTelemetry traces, metrics, and logs. A hermetic demo stack lives at apps/backend/deployments/otel-demo/: OpenTelemetry Collector, Tempo, Prometheus, Loki, Grafana.
cd apps/backend/deployments/otel-demo
docker compose up -d
./smoke-backend.shEvery fga.authorize decision lands as a parent span with 5 child spans (one per FGA step). 9 SemConv attributes ride the parent: agent.id, agent.public_key.algorithm, agent.trust_score, agent.drift_score, agent.scan_verdict, agent.capability, fga.step, fga.outcome, fga.denied_by. The attribute set is proposed to the OpenTelemetry Semantic Conventions WG. Full design notes: apps/backend/docs/OBSERVABILITY.md.
Four runnable demos in examples/:
| Demo | Shows | Stack |
|---|---|---|
flight-search-agent |
Python SDK with three deterministic prompt-injection scenarios (inject data-exfil, inject priv-esc, inject sandbox-escape) |
Python + AIM server |
langchain-crud-agent |
LangChain agent secured by @perform_action |
Python + LangChain + AIM server |
mcp-server-demo |
MCP server with Ed25519 signing | Python + Flask + PyNaCl |
a2a-multi-agent-demo |
A2A collaboration: discovery, GDPR consent, request signing, skill attestation | Python + Java + AIM server |
Same code, run twice, with and without AIM: the RAGBot-AIM A/B demo in DVAA, the intentionally vulnerable agent platform, lands a prompt injection on both runs and shows AIM deny the outbound exfil on the protected one because http:post is outside the grant (recording).
| Guide | Time |
|---|---|
| Register an agent | 2 min |
| Audit agent actions | 5 min |
| Enforce capabilities | 5 min |
| Embed in an app | 10 min |
| Fleet governance | 30 min |
Full index: docs/USE-CASES.md.
Apache 2.0. PRs from outside the org welcome. CONTRIBUTING.md has the dev loop and the pull request process.
Security issues: info@opena2a.org. Coordinated disclosure; see SECURITY.md for what to expect and when.
Part of the OpenA2A security platform.
Apache-2.0. See LICENSE.