Get a local AgentWrit broker running and issue your first agent token. By the end, you'll have walked through the full registration flow — admin authentication, launch token creation, challenge-response identity, and token issuance.
Prerequisites: Docker, curl, and basic terminal familiarity. About 15 minutes.
AgentWrit is a security service that issues short-lived, scoped identity tokens to autonomous agents. Think of it like a hotel key card system: when you check in, you receive a key card that only works for your assigned room and automatically expires at checkout time. Similarly, AgentWrit issues agents temporary credentials that grant access only to the specific resources they need, and the credentials expire quickly to limit the damage if they're compromised. This design ensures agents can't accidentally (or maliciously) access resources beyond their authorization, and even if credentials leak, they expire within minutes.
AgentWrit serves three different personas. Which one are you?
| If you are... | Read this | Why |
|---|---|---|
| Building or integrating an AI agent in Python, TypeScript, Go, or another language | Getting Started: Developer | Developers focus on requesting tokens and using them in agent code. |
| Deploying and operating AgentWrit in production (setting up brokers, configuring scopes) | Getting Started: Operator | Operators manage the full deployment: broker security, launch token creation, and monitoring. |
| Just trying AgentWrit locally to understand how it works end-to-end | This guide | You'll run a local setup with Docker Compose, then walk through the agent registration flow to see how it works. |
Before you begin, verify you have the required tools. Run these commands in your terminal:
# Check Docker
docker --version
# Expected: Docker version 20.10+ (anything recent is fine)
# Check Docker Compose
docker-compose --version
# Expected: docker-compose version 1.29+
# Check curl
curl --version
# Expected: curl 7.x or later
# Check Go (optional, only needed for local builds)
go version
# Expected: go version 1.24+ (not required if using Docker)If any of these are missing, install them:
- Docker & Docker Compose: https://docs.docker.com/get-docker/
- curl: Usually pre-installed on macOS and Linux. On Windows, use WSL2 or Git Bash.
- Go: https://go.dev/dl (optional, only needed if you want to build locally)
Docker Compose is the easiest way to get started. It launches the broker in two commands.
# 1. Clone the repository
git clone https://github.com/devonartis/agentwrit.git
cd agentwrit
# 2. Set the admin secret (required -- broker exits without it)
export AA_ADMIN_SECRET="$(openssl rand -hex 32)"
# 3. Start the stack
./scripts/stack_up.shThis starts the core service:
- Broker on port 8080 (the security service)
curl http://localhost:8080/v1/healthExpected response:
{"status":"ok","version":"2.0.0","uptime":5,"db_connected":true,"audit_events_count":0}If you prefer to build and run locally without Docker:
# 1. Clone the repository
git clone https://github.com/devonartis/agentwrit.git
cd agentwrit
# 2. Build the Go binaries
go build ./...
# 3. Start the broker
export AA_ADMIN_SECRET="$(openssl rand -hex 32)"
go run ./cmd/brokerThe broker will log to stdout. You should see a "broker started" message.
AgentWrit provides a single registration flow to get tokens. Here's the big picture:
The flow has two phases: the operator bootstrap (steps 1–2) where the admin authenticates and creates a launch token, and the agent registration (steps 3–6) where the agent proves its identity and gets a scoped JWT. You'll walk through each step below.
flowchart LR
subgraph You["What You'll Do"]
direction TB
S1["1️⃣ Authenticate<br/>as admin"]
S2["2️⃣ Create<br/>launch token"]
S3["3️⃣ Get nonce<br/>challenge"]
S4["4️⃣ Sign nonce<br/>with Ed25519"]
S5["5️⃣ Register<br/>with broker"]
S6["6️⃣ Use JWT<br/>as Bearer token"]
S1 --> S2 --> S3 --> S4 --> S5 --> S6
end
classDef step fill:#0f3460,stroke:#53a8b6,color:#fff
class S1,S2,S3,S4,S5,S6 step
For the full technical sequence diagram, see Architecture → Data Flow Diagrams.
If you want to see the underlying mechanics and control your own Ed25519 keys, follow this path. It takes more steps, but every operation is explicit and transparent.
First, obtain an admin token using the shared admin secret:
curl -s -X POST http://localhost:8080/v1/admin/auth \
-H "Content-Type: application/json" \
-d "{\"secret\": \"$AA_ADMIN_SECRET\"}"Response:
{
"access_token": "eyJhbGciOiJFZERTQSIs...",
"expires_in": 300,
"token_type": "Bearer"
}Save the admin token:
ADMIN_TOKEN=$(curl -s -X POST http://localhost:8080/v1/admin/auth \
-H "Content-Type: application/json" \
-d "{\"secret\": \"$AA_ADMIN_SECRET\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
echo "Admin token saved: $ADMIN_TOKEN"A launch token is a one-time credential that authorizes your agent to register with the broker. It includes the scopes the agent is allowed to request.
curl -s -X POST http://localhost:8080/v1/admin/launch-tokens \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{
"agent_name": "my-agent",
"allowed_scope": ["read:data:*"],
"max_ttl": 300,
"single_use": true,
"ttl": 30
}'Response:
{
"launch_token": "a1b2c3d4e5f6...64-hex-chars",
"expires_at": "2026-02-15T12:00:30Z",
"policy": {
"allowed_scope": ["read:data:*"],
"max_ttl": 300
}
}Save the launch token:
LAUNCH_TOKEN="a1b2c3d4e5f6...paste-your-value-here"Important: This launch token expires in 30 seconds. Complete the next steps before it expires.
The broker issues a random nonce (challenge) that your agent must sign. This proves your agent holds its private key.
curl -s http://localhost:8080/v1/challengeResponse:
{
"nonce": "7f3a9c1b4d2e8f0a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8",
"expires_in": 30
}Save the nonce:
NONCE="7f3a9c1b4d2e8f0a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8"Important: This nonce also expires in 30 seconds. Keep moving!
Generate an Ed25519 key pair and sign the nonce bytes with your private key. This is the "proof of identity" step.
Using Python with the cryptography library:
python3 << 'EOF'
import base64, json
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
# Generate Ed25519 key pair
private_key = Ed25519PrivateKey.generate()
public_key = private_key.public_key()
# Export public key as raw 32 bytes, then base64 encode
pub_bytes = public_key.public_bytes(Encoding.Raw, PublicFormat.Raw)
pub_b64 = base64.b64encode(pub_bytes).decode()
# Sign the nonce (IMPORTANT: hex-decode the nonce first!)
nonce_hex = '7f3a9c1b4d2e8f0a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8'
nonce_bytes = bytes.fromhex(nonce_hex)
signature = private_key.sign(nonce_bytes)
sig_b64 = base64.b64encode(signature).decode()
# Output the keys and signature
output = {
'public_key': pub_b64,
'signature': sig_b64,
'nonce': nonce_hex
}
print(json.dumps(output, indent=2))
# Save for the next step
import os
print(f"\nExport these for step 5:", file=os.sys.stderr)
print(f"PUBLIC_KEY={pub_b64}", file=os.sys.stderr)
print(f"SIGNATURE={sig_b64}", file=os.sys.stderr)
EOFSave the output values:
PUBLIC_KEY="<base64-encoded-public-key>"
SIGNATURE="<base64-encoded-signature>"Now register your agent with the broker. You're submitting:
- The launch token (authorizes registration)
- The public key (your agent's identity)
- The signature (proof you hold the private key)
- The nonce (the challenge you signed)
curl -s -X POST http://localhost:8080/v1/register \
-H "Content-Type: application/json" \
-d '{
"launch_token": "'"$LAUNCH_TOKEN"'",
"nonce": "'"$NONCE"'",
"public_key": "'"$PUBLIC_KEY"'",
"signature": "'"$SIGNATURE"'",
"orch_id": "my-orchestrator",
"task_id": "task-001",
"requested_scope": ["read:data:*"]
}'Response:
{
"agent_id": "spiffe://agentwrit.local/agent/my-orchestrator/task-001/a1b2c3d4e5f6a7b8",
"access_token": "eyJhbGciOiJFZERTQSIs...",
"expires_in": 300
}The access_token is your agent's credential. Use it in the Authorization header for authenticated requests:
AGENT_TOKEN="eyJhbGciOiJFZERTQSIs..."
# Example: Make an authenticated request to a downstream API
curl -s https://your-api.example.com/data \
-H "Authorization: Bearer $AGENT_TOKEN"You completed AgentWrit's identity and authorization flow. Here's what happened:
- Admin Authentication → You authenticated as the admin using the shared secret
- Launch Token Creation → The broker issued a single-use registration credential
- Challenge Issued → The broker sent a random nonce for you to sign
- Proof of Identity → You signed the nonce with an Ed25519 private key, proving you hold the key
- Agent Registration → The broker verified your signature and issued a SPIFFE ID
- Token Exchange → You received a short-lived JWT scoped to your requested permissions
Key design decisions:
- Tokens are short-lived (default 5 minutes) to limit the window if credentials leak
- Tokens are scoped (e.g.,
read:data:*) so agents can't access resources beyond their authorization - Tokens are revocable if you suspect compromise
- SPIFFE IDs provide a standard, verifiable identity format
You now understand how AgentWrit works. Here are your next steps:
If you're building an agent in Python, TypeScript, Go, or another language:
- Getting Started: Developer -- Learn how to integrate AgentWrit into your agent code
- Common Tasks -- Token renewal, delegation, revocation
If you're deploying AgentWrit in production:
- Getting Started: Operator -- Deploy the broker, manage scopes, configure persistence
- Architecture -- Understand the internals and security design
Want to see AgentWrit in a full application instead of raw curl commands?
- MedAssist AI Demo -- Healthcare multi-agent pipeline with LLM tool-calling, delegation, and per-patient scoping
- Support Ticket Demo -- Three agents processing support tickets with streaming execution and natural token expiry
To deepen your understanding:
- Concepts -- The security model, SPIFFE IDs, scope matching, token lifecycle
- Troubleshooting -- Common errors and how to fix them
- API Reference -- Complete endpoint documentation with all parameters
- Scenarios -- End-to-end walkthroughs for common use cases
| Task | Guide |
|---|---|
| Request a token in code | Getting Started: Developer |
| Renew tokens before expiry | Common Tasks |
| Validate tokens | Getting Started: Developer |
| Revoke credentials | Common Tasks |
| Query audit logs | Common Tasks |
| Deploy in production | Getting Started: Operator |
| Understand SPIFFE IDs | Concepts |
When you're done experimenting, clean up the Docker containers:
./scripts/stack_down.shThis removes the broker container and its volumes.
-
Signing the hex string instead of bytes -- The nonce is a 64-character hex string, but you must hex-decode it to 32 bytes before signing.
# WRONG signature = private_key.sign(nonce_hex.encode()) # CORRECT signature = private_key.sign(bytes.fromhex(nonce_hex))
-
Using DER-encoded keys -- The broker expects raw 32-byte Ed25519 public keys, not PEM or DER.
# WRONG (DER is too long) pub_key = key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo) # CORRECT (raw is exactly 32 bytes) pub_key = key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)
-
Expired nonce or launch token -- Both expire in 30 seconds. If you get a "nonce not found" or "launch token invalid" error, get a fresh one and retry immediately.
-
Wrong admin secret -- Verify the
AA_ADMIN_SECRETmatches what you set when starting the broker.
Problem: curl http://localhost:8080/v1/health returns connection refused.
Solution:
- Is Docker running? Check
docker ps. - Did
./scripts/stack_up.shcomplete successfully? Check for errors in the output. - Is port 8080 already in use? Run
lsof -i :8080(macOS/Linux) ornetstat -ano | findstr :8080(Windows).
Problem: Admin auth returns {"error": "invalid credentials"}.
Solution:
- Double-check
AA_ADMIN_SECRETmatches what you set in theexportcommand. - Is it exported in the same terminal? Run
echo $AA_ADMIN_SECRETto verify.
Problem: Registration fails with "nonce not found" or "nonce expired".
Solution:
- Nonces expire in 30 seconds. Don't pause between steps 3 and 5. If you need to restart, just get a fresh nonce from step 3.
Problem: Your agent uses the token but gets 401 or 403.
Solution:
- Is the token still valid? Check
curl http://localhost:8080/v1/token/validate(see API Reference). - Does the scope match what the API requires? The token includes a
scopeclaim. Verify it covers the action. - Is the Bearer token in the Authorization header? Use
Bearer $TOKEN, not just the token.
For more detailed error messages and solutions, see Troubleshooting.
You've seen the full registration flow. Pick your path:
Getting Started: Developer → Integrate AgentWrit into your agent code — Python, TypeScript, or Go.
Getting Started: Operator → Deploy the broker in production — TLS, persistence, monitoring.
Or go deeper on the concepts:
| If you want to... | Read this |
|---|---|
| Understand what tokens are and how JWTs work | Foundations |
| See who holds which token and why | The Three Actors |
| Learn the scope system | Scopes and Permissions |
| Renew, delegate, or revoke tokens | Common Tasks |
| Look up a specific endpoint | API Reference |
Previous: What Is AgentWrit? · Next: Getting Started: Developer
