Deployment Guide¶
This guide covers how to deploy AgentPin discovery and revocation endpoints in production.
Architecture Overview¶
Internet Your Infrastructure
───────── ──────────────────
Verifiers ──HTTPS──> Reverse Proxy ──> agentpin-server
(nginx/Apache) (port 8080)
│
├── /.well-known/agent-identity.json
└── /.well-known/agent-identity-revocations.json
AgentPin endpoints are static JSON documents served over HTTPS. You can either:
- Use
agentpin-server— The built-in Axum HTTP server - Serve static files — Place JSON documents in your web server's document root
- Use a CDN — Serve from S3/CloudFront or similar
Option 1: Using agentpin-server¶
Build¶
Run¶
Endpoints¶
| Path | Cache-Control | Description |
|---|---|---|
GET /.well-known/agent-identity.json |
max-age=3600 |
Discovery document |
GET /.well-known/agent-identity-revocations.json |
max-age=300 |
Revocation document |
GET /health |
— | Health check (200 OK) |
Systemd Service¶
# /etc/systemd/system/agentpin.service
[Unit]
Description=AgentPin Discovery Server
After=network.target
[Service]
Type=simple
User=agentpin
Group=agentpin
WorkingDirectory=/opt/agentpin
ExecStart=/usr/local/bin/agentpin-server \
--discovery /opt/agentpin/agent-identity.json \
--revocation /opt/agentpin/revocations.json \
--port 8080
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Option 2: Static Files¶
Place discovery and revocation documents directly in your web server's .well-known directory:
Nginx Configuration¶
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;
# AgentPin discovery document
location = /.well-known/agent-identity.json {
root /var/www/example.com;
default_type application/json;
add_header Cache-Control "public, max-age=3600";
add_header Access-Control-Allow-Origin "*";
add_header X-Content-Type-Options "nosniff";
}
# AgentPin revocation document
location = /.well-known/agent-identity-revocations.json {
root /var/www/example.com;
default_type application/json;
add_header Cache-Control "public, max-age=300";
add_header Access-Control-Allow-Origin "*";
add_header X-Content-Type-Options "nosniff";
}
}
Apache Configuration¶
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.pem
SSLCertificateKeyFile /etc/ssl/private/example.com.key
# AgentPin discovery
<Location "/.well-known/agent-identity.json">
Header set Cache-Control "public, max-age=3600"
Header set Content-Type "application/json"
Header set Access-Control-Allow-Origin "*"
Header set X-Content-Type-Options "nosniff"
</Location>
# AgentPin revocations
<Location "/.well-known/agent-identity-revocations.json">
Header set Cache-Control "public, max-age=300"
Header set Content-Type "application/json"
Header set Access-Control-Allow-Origin "*"
Header set X-Content-Type-Options "nosniff"
</Location>
</VirtualHost>
Creating the Discovery Document¶
Step 1: Generate Keys¶
Step 2: Build the Document¶
Create agent-identity.json:
{
"agentpin_version": "0.1",
"entity": "example.com",
"entity_type": "maker",
"public_keys": [
{
"kid": "example-2026-01",
"kty": "EC",
"crv": "P-256",
"x": "<from example-2026-01.public.jwk.json>",
"y": "<from example-2026-01.public.jwk.json>",
"use": "sig",
"key_ops": ["verify"],
"exp": "2027-02-15T00:00:00Z"
}
],
"agents": [
{
"agent_id": "urn:agentpin:example.com:my-agent",
"name": "My Agent",
"description": "Production AI agent for data analysis",
"version": "1.0.0",
"capabilities": ["read:data", "write:reports", "execute:analysis"],
"credential_ttl_max": 3600,
"status": "active"
}
],
"revocation_endpoint": "https://example.com/.well-known/agent-identity-revocations.json",
"policy_url": "https://example.com/agent-policy",
"max_delegation_depth": 2,
"updated_at": "2026-02-15T00:00:00Z"
}
Or use the SDK:
from agentpin import build_discovery_document, pem_to_jwk, generate_key_pair, generate_key_id
import json
private_key_pem, public_key_pem = generate_key_pair()
kid = generate_key_id(public_key_pem)
jwk = pem_to_jwk(public_key_pem, kid)
discovery = build_discovery_document(
"example.com", "maker", [jwk],
[{
"agent_id": "urn:agentpin:example.com:my-agent",
"name": "My Agent",
"capabilities": ["read:data", "write:reports"],
"status": "active",
}],
2,
)
with open("agent-identity.json", "w") as f:
json.dump(discovery, f, indent=2)
Step 3: Create Revocation Document¶
Create an empty revocation document:
{
"agentpin_version": "0.1",
"entity": "example.com",
"revoked_credentials": [],
"revoked_agents": [],
"revoked_keys": [],
"updated_at": "2026-02-15T00:00:00Z"
}
Step 4: Deploy¶
# Copy to web server
sudo cp agent-identity.json /var/www/example.com/.well-known/
sudo cp revocations.json /var/www/example.com/.well-known/agent-identity-revocations.json
# Verify it's accessible
curl -s https://example.com/.well-known/agent-identity.json | jq .
Cache Headers¶
AgentPin specifies recommended cache durations:
| Document | Cache-Control | Rationale |
|---|---|---|
| Discovery | max-age=3600 (1 hour) |
Keys don't change often |
| Discovery (during rotation) | max-age=300 (5 min) |
Faster propagation during key rotation |
| Revocation | max-age=300 (5 min) |
Revocations need faster propagation |
Verifiers MUST re-fetch the discovery document when encountering an unknown kid in a credential.
HTTPS Requirements¶
- Discovery documents MUST be served over HTTPS with a valid TLS certificate
- HTTP redirects MUST NOT be followed (to prevent redirect-based attacks)
- The
entityfield in the discovery document MUST match the domain serving it
Docker Deployment¶
FROM rust:1.75-slim AS builder
WORKDIR /app
COPY . .
RUN cargo build --release -p agentpin-server
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/agentpin-server /usr/local/bin/
COPY agent-identity.json /data/
COPY revocations.json /data/
EXPOSE 8080
CMD ["agentpin-server", "--discovery", "/data/agent-identity.json", "--revocation", "/data/revocations.json", "--port", "8080"]
Monitoring¶
Health Check¶
Validation Script¶
#!/bin/bash
# validate-endpoints.sh — Verify AgentPin endpoints are serving correctly
DOMAIN="${1:-example.com}"
echo "Checking discovery document..."
DISCOVERY=$(curl -sf "https://$DOMAIN/.well-known/agent-identity.json")
if [ $? -ne 0 ]; then
echo "FAIL: Cannot fetch discovery document"
exit 1
fi
# Validate entity matches domain
ENTITY=$(echo "$DISCOVERY" | jq -r '.entity')
if [ "$ENTITY" != "$DOMAIN" ]; then
echo "FAIL: entity '$ENTITY' doesn't match domain '$DOMAIN'"
exit 1
fi
# Validate at least one public key
KEY_COUNT=$(echo "$DISCOVERY" | jq '.public_keys | length')
if [ "$KEY_COUNT" -lt 1 ]; then
echo "FAIL: No public keys in discovery document"
exit 1
fi
echo "OK: Discovery document valid ($KEY_COUNT keys, $(echo "$DISCOVERY" | jq '.agents | length') agents)"
echo "Checking revocation document..."
curl -sf "https://$DOMAIN/.well-known/agent-identity-revocations.json" > /dev/null
if [ $? -ne 0 ]; then
echo "WARN: No revocation document (recommended but optional)"
else
echo "OK: Revocation document accessible"
fi
Key Rotation¶
When rotating keys:
- Generate a new key pair
- Add the new key to the discovery document's
public_keysarray (keep the old key) - Reduce cache TTL to 5 minutes during transition
- Start issuing credentials with the new key
- After the transition period, add the old key to the revocation document
- Remove the old key from the discovery document
- Restore normal cache TTL
# 1. Generate new key
agentpin keygen --domain example.com --kid example-2026-02 --output-dir ./keys
# 2. Update discovery document (add new key, keep old)
# Edit agent-identity.json
# 3-6. Issue new credentials, revoke old key, clean up
# ...
# 7. Verify rotation completed
curl -s https://example.com/.well-known/agent-identity.json | jq '.public_keys[].kid'