Python SDK for the AgentWrit broker -- ephemeral scoped credentials for AI agents via Ed25519 challenge-response
Project description
AgentWrit Python SDK
Ephemeral, task-scoped credentials for AI agents.
Built on Ed25519 challenge-response and the Ephemeral Agent Credentialing v1.3 pattern.
Why · Install · Prerequisites · Quick Start · Lifecycle · Demo · Scopes · Delegation · Errors · Architecture · Docs
Why AgentWrit?
AI agents need credentials to access databases, APIs, and file systems. Most teams give agents shared API keys or inherit user permissions — both create over-privileged, long-lived, unauditable access. AgentWrit takes a different approach:
- Ephemeral identities — every agent gets a unique Ed25519 keypair, generated in memory and never persisted to disk
- Task-scoped tokens — credentials are limited to exactly what the agent needs (
read:data:customers, notread:*:*) - Short-lived by default — tokens expire in minutes, not hours or days
- Delegation chains — agents can delegate a subset of their permissions to other agents; the broker rejects any attempt to widen
This SDK is the Python client for the AgentWrit broker — the broker is the credential authority, and this SDK is how your Python code talks to it.
Installation
Install from GitHub (not yet on PyPI):
uv add git+https://github.com/devonartis/agentwrit-python.git
Or with pip:
pip install git+https://github.com/devonartis/agentwrit-python.git
For local development:
git clone https://github.com/devonartis/agentwrit-python.git
cd agentwrit-python
uv sync --all-extras
Requirements: Python 3.10+. The SDK also needs a broker and credentials — see Prerequisites.
Prerequisites
The SDK is a client. It does not run the broker, and it does not mint its own credentials. Before any code in Quick Start will work, you need three things:
1. A reachable AgentWrit broker. The broker is a separate service that issues and validates tokens.
- Have a platform team running one? Ask them for the broker URL.
- Running it yourself? Stand one up locally — the broker repo ships a
docker composesetup. From this repo:docker compose up -d # pulls devonartis/agentwrit from Docker Hub
2. App credentials (client_id + client_secret).
These are issued by the broker operator/admin when they register your app and set its scope ceiling. The SDK cannot create them for you.
- Have a broker admin? Ask them to register your app and send you the
client_idandclient_secret. - You are the admin? Use the included setup script (it registers an app and prints both values):
export AGENTWRIT_ADMIN_SECRET="<your-broker-admin-secret>" uv run python demo/setup.py
3. Environment variables set on the process that uses the SDK:
export AGENTWRIT_BROKER_URL="http://localhost:8080" # from step 1
export AGENTWRIT_CLIENT_ID="<from step 2>"
export AGENTWRIT_CLIENT_SECRET="<from step 2>"
Auth is lazy — the SDK doesn't talk to the broker until your first
create_agent()call. If that call raisesAuthenticationError, yourclient_idorclient_secretis wrong (or the operator rotated them). If it raisesTransportError, the broker URL is unreachable.
Quick Start
Assumes Prerequisites are met — broker reachable, app registered, env vars set.
import os
from agentwrit import AgentWritApp, validate
# Connect to the broker (lazy — no auth until first create_agent)
app = AgentWritApp(
broker_url=os.environ["AGENTWRIT_BROKER_URL"],
client_id=os.environ["AGENTWRIT_CLIENT_ID"], # from broker admin
client_secret=os.environ["AGENTWRIT_CLIENT_SECRET"], # from broker admin
)
# Create an agent with specific scope
agent = app.create_agent(
orch_id="my-service",
task_id="read-customer-data",
requested_scope=["read:data:customers"],
)
# Use the token as a Bearer credential
import httpx
resp = httpx.get(
"https://your-api/data/customers",
headers=agent.bearer_header,
)
# Validate the token (any service can do this)
result = validate(app.broker_url, agent.access_token)
print(result.claims.scope) # ['read:data:customers']
# Release when done — token is dead immediately
agent.release()
Agent Lifecycle
# Create — agent gets a SPIFFE identity and scoped JWT
agent = app.create_agent(orch_id="svc", task_id="task", requested_scope=["read:data:x"])
# Use — agent.access_token is a standard Bearer JWT
print(agent.agent_id) # spiffe://agentwrit.local/agent/svc/task/a1b2c3d4
print(agent.scope) # ['read:data:x']
print(agent.expires_in) # 300 (seconds)
# Renew — new token, same identity, old token revoked
agent.renew()
# Delegate — pass a subset of scope to another agent (equal or narrower)
delegated = agent.delegate(delegate_to=other.agent_id, scope=["read:data:x"])
# Release — self-revoke, idempotent
agent.release()
MedAssist AI Demo
The demo/ directory contains MedAssist AI — an interactive healthcare demo that showcases every AgentWrit capability against a live broker.
What it does: A FastAPI web app where you enter a patient ID and a plain-language request. A local LLM (OpenAI-compatible) chooses which tools to call, and the app dynamically creates broker agents with only the scopes those tools need for that specific patient. Every step — scope enforcement, cross-patient denial, delegation, token renewal, release — appears in a real-time execution trace.
What it demonstrates:
| Capability | How the demo shows it |
|---|---|
| Dynamic agent creation | Agents spawn on demand as the LLM selects tools — clinical, billing, prescription |
| Per-patient scope isolation | Each agent's scopes are parameterized to one patient ID |
| Cross-patient denial | LLM asks for another patient's records → scope_denied in the trace |
| Delegation | Clinical agent delegates write:prescriptions:{patient} to the prescription agent |
| Token lifecycle | Renewal and release shown at end of each encounter |
| Audit trail | Dedicated audit tab showing hash-chained broker events |
Running the demo
# 1. Start the AgentWrit broker
docker compose up -d
# 2. Register the demo app with the broker (one-time setup)
export AGENTWRIT_ADMIN_SECRET="your-admin-secret"
uv run python demo/setup.py
# → Prints client_id and client_secret
# 3. Configure demo/.env (copy from demo/.env.example)
cp demo/.env.example demo/.env
# Fill in: broker URL, client_id, client_secret, LLM endpoint
# 4. Run it
uv run uvicorn demo.app:app --reload --port 5000
# Open http://127.0.0.1:5000
For architecture diagrams, step-by-step traces, and a live presentation script, see demo/BEGINNERS_GUIDE.md and demo/PRESENTERS_GUIDE.md.
Scope Format
Scopes are three segments: action:resource:identifier
read:data:customers — read customer data
write:data:order-abc-123 — write to a specific order
read:data:* — wildcard: read ANY data resource
Wildcard * only works in the identifier (third) position. Action and resource must match exactly.
from agentwrit import scope_is_subset
scope_is_subset(["read:data:customers"], ["read:data:*"]) # True
scope_is_subset(["write:data:customers"], ["read:data:*"]) # False (write != read)
scope_is_subset(["read:logs:customers"], ["read:data:*"]) # False (logs != data)
Delegation
Agents delegate a subset of their scope to other agents. Delegation cannot widen authority — equal or narrower scope is accepted; any scope the delegator doesn't hold is rejected.
# A has broad scope
agent_a = app.create_agent(
orch_id="pipeline", task_id="orchestrator",
requested_scope=["read:data:partition-7", "read:data:partition-8"],
)
# A delegates ONLY partition-7 to B
delegated = agent_a.delegate(
delegate_to=agent_b.agent_id,
scope=["read:data:partition-7"],
)
# Validate: delegated token has only partition-7
result = validate(app.broker_url, delegated.access_token)
print(result.claims.scope) # ['read:data:partition-7']
Error Handling
from agentwrit.errors import AuthorizationError, TransportError
try:
agent = app.create_agent(orch_id="svc", task_id="t", requested_scope=scope)
except AuthorizationError as e:
print(e.status_code) # 403
print(e.problem.detail) # "scope exceeds app ceiling"
print(e.problem.error_code) # "scope_violation"
except TransportError:
print("Broker unreachable")
Architecture
graph TB
subgraph App["Your Application"]
direction TB
Client["AgentWritApp"]
end
subgraph Broker["AgentWrit Broker"]
direction LR
AuthGroup["App Auth<br/>/v1/app/auth<br/>/v1/app/launch-tokens"]
CredGroup["Credentials<br/>/v1/challenge<br/>/v1/register"]
MgmtGroup["Management<br/>/v1/delegate<br/>/v1/token/validate<br/>/v1/token/renew<br/>/v1/token/release"]
end
Agents["Your AI Agents"]
APIs["Protected APIs"]
Client ==>|"HTTPS"| Broker
Client -.->|"create_agent()"| Agents
Agents ==>|"Bearer JWT"| APIs
style App fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a5f
style Broker fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#78350f
style Agents fill:#d1fae5,stroke:#10b981,stroke-width:2px
style APIs fill:#ede9fe,stroke:#8b5cf6,stroke-width:2px
Authority Chain
Operator (root of trust)
│ registers app, sets scope ceiling
▼
Application (your code — AgentWritApp)
│ creates agents within ceiling
▼
Agent (ephemeral SPIFFE identity + scoped JWT)
│ delegation cannot widen scope (equal or narrower allowed)
▼
Delegated Agent (sub-agent, max 5 hops)
Documentation
| Guide | Description |
|---|---|
| Concepts | Roles, scopes, delegation, trust model, and standards |
| Getting Started | Install, connect, and create your first agent |
| Developer Guide | Delegation patterns, scope gating, error handling |
| API Reference | Every class, method, parameter, and exception |
| Testing Guide | Unit tests, integration tests, running the test suite |
For broker setup and administration, see the AgentWrit broker documentation.
Standards Alignment
| Standard | What it addresses |
|---|---|
| NIST IR 8596 | Unique AI agent identities via SPIFFE IDs |
| NIST SP 800-207 | Zero-trust per-request validation |
| OWASP Top 10 for Agentic AI (2026) | ASI03 (Identity/Privilege Abuse), ASI07 (Insecure Inter-Agent Communication) |
| IETF WIMSE | Delegation chain re-binding |
| IETF draft-klrc-aiagent-auth-00 | OAuth/WIMSE/SPIFFE framework for AI agents |
Contributing
See CONTRIBUTING.md for the full workflow: uv setup, live-broker verification (clone agentwrit or use your own broker), and evidence to include in PRs so maintainers can review broker-facing changes confidently.
Quick local checks (no broker required for unit tests):
git clone https://github.com/devonartis/agentwrit-python.git
cd agentwrit-python
uv sync --all-extras
uv run ruff check .
uv run mypy --strict src/
uv run pytest tests/unit/
License
This SDK is licensed under the MIT License.
The AgentWrit broker is licensed separately under PolyForm Internal Use 1.0.0. See the broker repo for details.
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 agentwrit-0.3.0.tar.gz.
File metadata
- Download URL: agentwrit-0.3.0.tar.gz
- Upload date:
- Size: 5.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.4.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
699eb63fd2d65b9607df1d4f772c6873a42dac02b9e7fb509c65703218ca5d86
|
|
| MD5 |
17cc49624b982d5391c7113643de3434
|
|
| BLAKE2b-256 |
f77cb9f0a74f79153c3cf2efecb4fb3111ca32dd2e2e571d73ad99f907cc6229
|
File details
Details for the file agentwrit-0.3.0-py3-none-any.whl.
File metadata
- Download URL: agentwrit-0.3.0-py3-none-any.whl
- Upload date:
- Size: 21.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.4.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
87abd32fbcdf819e19acde7b6a0e0d0134fb8690fff5b5226a624597b12b7672
|
|
| MD5 |
46774d71c023727d02444259aeace3c7
|
|
| BLAKE2b-256 |
92c1b290a307b5c9206fc379f79093b592f4fbc891a57825e663218be4f45365
|