The IDProva Registry is an HTTP server built with Axum. It provides AID document storage and DAT verification services.
Base URL: http://localhost:3000 (default port, configurable via REGISTRY_PORT)
Rate limiting: 120 requests per 60-second window per IP.
Request body limit: 1 MB.
Security headers: All responses include X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security, and X-XSS-Protection.
Write endpoints (PUT, DELETE, POST /v1/dat/revoke) require a valid admin DAT when REGISTRY_ADMIN_PUBKEY is set.
Authorization: Bearer <compact-jws-dat>
In development mode (no REGISTRY_ADMIN_PUBKEY), write endpoints are open with a warning logged.
Health check. No authentication required.
Response 200:
{
"status": "ok",
"version": "0.1.0",
"protocol": "idprova/0.1"
}curl example:
curl http://localhost:3000/healthReadiness probe — checks database connectivity. No authentication required.
Response 200:
{
"status": "ready",
"db": "ok"
}Response 503 Service Unavailable:
{
"status": "not_ready",
"db": "error"
}curl example:
curl http://localhost:3000/readyProtocol metadata. No authentication required.
Response 200:
{
"protocolVersion": "0.1",
"registryVersion": "0.1.0",
"didMethod": "did:aid",
"supportedAlgorithms": ["EdDSA"],
"supportedHashAlgorithms": ["blake3", "sha-256"]
}curl example:
curl http://localhost:3000/v1/metaRegister or update an AID document. Requires authentication in production mode.
The :id path parameter is the DID suffix after did:aid: — e.g. for did:aid:example.com:my-agent, use example.com:my-agent.
Request body: AID document JSON (see Protocol Specification for schema).
Response 201 Created (new AID):
{
"id": "did:aid:example.com:my-agent",
"status": "created"
}Response 200 OK (updated existing AID):
{
"id": "did:aid:example.com:my-agent",
"status": "updated"
}Response 400 Bad Request:
{ "error": "invalid AID document: ..." }Response 401 Unauthorized:
{ "error": "Authorization header required for write operations" }curl example:
# Development mode
curl -X PUT http://localhost:3000/v1/aid/example.com:my-agent \
-H "Content-Type: application/json" \
-d @my-agent.aid.json
# Production mode
curl -X PUT http://localhost:3000/v1/aid/example.com:my-agent \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <admin-dat>" \
-d @my-agent.aid.jsonResolve an AID document. No authentication required.
Response 200:
{
"id": "did:aid:example.com:my-agent",
"controller": "did:aid:example.com:operator",
"verificationMethod": [
{
"id": "did:aid:example.com:my-agent#key-ed25519",
"type": "Ed25519VerificationKey2020",
"controller": "did:aid:example.com:my-agent",
"publicKeyMultibase": "z6Mk..."
}
],
"metadata": {
"name": "My Agent"
}
}Response 404:
{ "error": "AID not found: did:aid:example.com:my-agent" }curl example:
curl http://localhost:3000/v1/aid/example.com:my-agentDeactivate (remove) an AID from the registry. Requires authentication in production mode.
Response 200:
{
"id": "did:aid:example.com:my-agent",
"status": "deactivated"
}Response 404:
{ "error": "AID not found: did:aid:example.com:my-agent" }curl example:
curl -X DELETE http://localhost:3000/v1/aid/example.com:my-agent \
-H "Authorization: Bearer <admin-dat>"Get the verification keys for an AID. No authentication required. Used by services for DAT verification without fetching the full document.
Response 200:
{
"id": "did:aid:example.com:my-agent",
"keys": [
{
"id": "did:aid:example.com:my-agent#key-ed25519",
"type": "Ed25519VerificationKey2020",
"publicKeyMultibase": "z6Mk..."
}
]
}Response 404:
{ "error": "AID not found: did:aid:example.com:my-agent" }curl example:
curl http://localhost:3000/v1/aid/example.com:my-agent/keyVerify a DAT token against the issuer's registered public key. No authentication required. The registry looks up the issuer AID, decodes the public key, and runs the full verification pipeline.
Request body:
{
"token": "<compact-jws>",
"scope": "mcp:tool:filesystem:read",
"request_ip": "203.0.113.42",
"trust_level": 1,
"delegation_depth": 0,
"actions_in_window": 0,
"country_code": "AU",
"agent_config_hash": "<sha256-hex>"
}| Field | Type | Required | Description |
|---|---|---|---|
token |
string | yes | Compact JWS token |
scope |
string | no | Required scope (empty = skip scope check) |
request_ip |
string | no | Client IP for IP allowlist/denylist constraints |
trust_level |
integer (0–100) | no | Agent trust level for min_trust_level constraint |
delegation_depth |
integer | no | Chain depth for max_delegation_depth constraint (default 0) |
actions_in_window |
integer | no | Actions in current rate-limit window (default 0) |
country_code |
string | no | ISO 3166-1 alpha-2 country code for geofencing |
agent_config_hash |
string | no | SHA-256 hex of agent config for required_config_hash constraint |
Response 200 (valid):
{
"valid": true,
"issuer": "did:aid:example.com:operator",
"subject": "did:aid:example.com:my-agent",
"scopes": ["mcp:tool:filesystem:read"],
"jti": "01234567-89ab-cdef-0123-456789abcdef"
}Response 200 (invalid):
{
"valid": false,
"issuer": "did:aid:example.com:operator",
"subject": "did:aid:example.com:my-agent",
"scopes": ["mcp:tool:filesystem:read"],
"jti": "01234567-89ab-cdef-0123-456789abcdef",
"error": "DAT has expired"
}Response 400 (malformed token):
{
"valid": false,
"error": "malformed token: ..."
}The registry checks revocation before any crypto work. If the JTI is revoked, valid: false is returned with the revocation details in error.
curl example:
curl -X POST http://localhost:3000/v1/dat/verify \
-H "Content-Type: application/json" \
-d '{
"token": "eyJhbGci...",
"scope": "mcp:tool:filesystem:read",
"trust_level": 1,
"delegation_depth": 0
}'Revoke a DAT by JTI. Requires authentication in production mode. Revocation is immediate and permanent — subsequent calls to POST /v1/dat/verify with a revoked JTI return valid: false.
Request body:
{
"jti": "01234567-89ab-cdef-0123-456789abcdef",
"reason": "key compromise",
"revoked_by": "did:aid:example.com:operator"
}| Field | Type | Max Length | Description |
|---|---|---|---|
jti |
string | 128 | Token ID to revoke (required) |
reason |
string | 512 | Human-readable revocation reason |
revoked_by |
string | 256 | DID or identifier of the revoking party |
Response 200:
{
"jti": "01234567-89ab-cdef-0123-456789abcdef",
"status": "revoked",
"reason": "key compromise",
"revoked_by": "did:aid:example.com:operator"
}Response 200 (already revoked):
{
"jti": "01234567-89ab-cdef-0123-456789abcdef",
"status": "already_revoked"
}Response 401: Authentication required.
curl example:
curl -X POST http://localhost:3000/v1/dat/revoke \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <admin-dat>" \
-d '{
"jti": "01234567-89ab-cdef-0123-456789abcdef",
"reason": "key compromise",
"revoked_by": "did:aid:example.com:operator"
}'List DAT revocations with pagination. No authentication required.
Query parameters:
| Param | Type | Default | Description |
|---|---|---|---|
limit |
integer | 50 | Max records to return (capped at 200) |
offset |
integer | 0 | Number of records to skip |
Response 200:
{
"revocations": [
{
"jti": "01234567-89ab-cdef-0123-456789abcdef",
"reason": "key compromise",
"revoked_by": "did:aid:example.com:operator",
"revoked_at": "2026-03-07T12:00:00Z"
}
],
"count": 1,
"limit": 50,
"offset": 0
}curl example:
curl "http://localhost:3000/v1/dat/revocations?limit=10&offset=0"Check whether a specific JTI has been revoked. No authentication required.
Response 200 (revoked):
{
"revoked": true,
"jti": "01234567-89ab-cdef-0123-456789abcdef",
"reason": "key compromise",
"revoked_by": "did:aid:example.com:operator",
"revoked_at": "2026-03-07T12:00:00Z"
}Response 200 (not revoked):
{
"revoked": false,
"jti": "01234567-89ab-cdef-0123-456789abcdef"
}curl example:
curl http://localhost:3000/v1/dat/revoked/01234567-89ab-cdef-0123-456789abcdefAll error responses use JSON with an error field:
{ "error": "description of what went wrong" }| Status | Meaning |
|---|---|
| 400 | Invalid request body or parameters |
| 401 | Missing or invalid authorization token |
| 404 | AID or JTI not found |
| 429 | Rate limit exceeded (max 120 req/min/IP); retry after 60s |
| 500 | Internal storage or verification error |
| Variable | Default | Description |
|---|---|---|
REGISTRY_PORT |
3000 |
TCP port to listen on |
REGISTRY_ADMIN_PUBKEY |
unset | 64-char hex Ed25519 public key for write auth. Unset = dev mode (open writes) |
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /health |
— | Health check |
| GET | /ready |
— | Readiness probe (DB check) |
| GET | /v1/meta |
— | Protocol metadata |
| PUT | /v1/aid/:id |
write | Register or update AID |
| GET | /v1/aid/:id |
— | Resolve AID |
| DELETE | /v1/aid/:id |
write | Deactivate AID |
| GET | /v1/aid/:id/key |
— | Get AID public keys |
| POST | /v1/dat/verify |
— | Verify a DAT |
| POST | /v1/dat/revoke |
write | Revoke a DAT by JTI |
| GET | /v1/dat/revocations |
— | List revocations (paginated) |
| GET | /v1/dat/revoked/:jti |
— | Check revocation status |