# NANDA Capability Token Service Mint scoped, time-boxed capability tokens, delegate strictly-narrower tokens to other agents without contacting the issuer, verify a presented token, and revoke a whole delegation subtree by revoking one ancestor. Base URL: https://nanda-capability-service.onrender.com The token is an opaque string. Treat it as a bearer capability: pass the exact string you receive; do not edit it. Tokens are macaroons — a child can only ever have equal-or-fewer scopes and an equal-or-shorter lifetime than its parent, and this is enforced when the token is verified. Scopes are arbitrary strings you choose (for example `tool:read`, `db:write`, `email:send`). `ttl_seconds` is how long the token stays valid. Revocation is kept in memory and resets if the service restarts. ## GET / Returns the service description and the list of endpoints. Also use it as a warm-up ping. Example call: curl -s https://nanda-capability-service.onrender.com/ Example response: {"service":"NANDA Capability Token Service","version":"1.0.0","description":"Mint scoped, time-boxed capability tokens; delegate strictly-narrower tokens to other agents without an issuer round-trip; verify; and revoke a whole delegation subtree by revoking one ancestor. Macaroon-based.","endpoints":{"POST /mint":"Issue a root capability token. Body: {subject, scopes[], ttl_seconds}","POST /attenuate":"Delegate a narrower child token. Body: {token, audience, scopes[], ttl_seconds}","POST /verify":"Verify a token. Body: {token, presenter?}","POST /revoke":"Revoke a token and its descendants. Body: {token}"},"skill":"/skill.md","status":"ok"} ## POST /mint Issue a new root capability token for a subject with a set of scopes and a lifetime. Example call: curl -s -X POST https://nanda-capability-service.onrender.com/mint \ -H 'content-type: application/json' \ -d '{"subject":"orchestrator","scopes":["tool:read","tool:write"],"ttl_seconds":600}' Example response: {"token":"eyJjaGFpbiI6W3sidGlkIjoiNThjNDg3...OPAQUE...","tid":"58c487546a6e8cc6","subject":"orchestrator","scopes":["tool:read","tool:write"],"expires_at":1783620556.77} ## POST /attenuate Delegate a strictly-narrower child token to another agent (the audience). The child's `scopes` must be a subset of the parent token's scopes; its lifetime is capped at the parent's. Requesting a scope the parent does not hold returns HTTP 400 with an `error` field. Example call: curl -s -X POST https://nanda-capability-service.onrender.com/attenuate \ -H 'content-type: application/json' \ -d '{"token":"","audience":"worker","scopes":["tool:read"],"ttl_seconds":300}' Example response: {"token":"eyJjaGFpbiI6W3sidGlkIjoiNThj...OPAQUE...","tid":"a1b2c3d4e5f60718","audience":"worker","scopes":["tool:read"],"expires_at":1783620256.44} ## POST /verify Verify a token. Returns `valid:true` with the granted scopes, or `valid:false` with a human-readable `reason`. Pass `presenter` (the agent id using the token) to also enforce audience binding: a token is only valid for the agent it was delegated to. Example call: curl -s -X POST https://nanda-capability-service.onrender.com/verify \ -H 'content-type: application/json' \ -d '{"token":"","presenter":"worker"}' Example response: {"valid":true,"subject":"worker","scopes":["tool:read"],"audience":"worker","expires_at":1783620256.44,"chain_depth":2} ## POST /authorize One call to decide access: returns `authorized:true` only if the token is valid AND grants the specific `scope` you name (and matches `presenter` if given). Use this at an access checkpoint instead of calling /verify and checking scopes yourself. Example call: curl -s -X POST https://nanda-capability-service.onrender.com/authorize \ -H 'content-type: application/json' \ -d '{"token":"","scope":"tool:read","presenter":"worker"}' Example response: {"authorized":true,"reason":"ok","subject":"worker","granted_scopes":["tool:read"],"expires_at":1783620256.44} If the token is valid but lacks the scope, you get `{"authorized":false,"reason":"token does not grant required scope 'tool:write'","granted_scopes":["tool:read"]}`. ## POST /inspect Decode a token's full delegation chain (root to leaf) for audit — who delegated what to whom, the scopes at each hop, expiry, and whether any hop is revoked — plus whether the signature is intact and the token is currently valid. No secret required. Example call: curl -s -X POST https://nanda-capability-service.onrender.com/inspect \ -H 'content-type: application/json' \ -d '{"token":""}' Example response: {"valid_signature":true,"currently_valid":true,"reason":"ok","chain_depth":2,"chain":[{"depth":0,"tid":"b2512dd6a4361202","parent_tid":null,"subject":"orchestrator","audience":"orchestrator","scopes":["tool:read","tool:write"],"issued_at":1783622733.62,"expires_at":1783623333.62,"revoked":false},{"depth":1,"tid":"2e9d3d43832331b9","parent_tid":"b2512dd6a4361202","subject":"worker","audience":"worker","scopes":["tool:read"],"issued_at":1783622733.62,"expires_at":1783623033.62,"revoked":false}]} ## POST /revoke Revoke a token by its id. Every token delegated from it (the whole subtree) fails verification immediately afterward. Example call: curl -s -X POST https://nanda-capability-service.onrender.com/revoke \ -H 'content-type: application/json' \ -d '{"token":""}' Example response: {"revoked":true,"tid":"58c487546a6e8cc6"} ## How an agent uses this service 1. Mint a root token: POST /mint with `subject` (your agent id), the `scopes` you want, and `ttl_seconds`. Save the `token` string from the response. 2. To grant a narrower capability to another agent, POST /attenuate with the parent `token`, the other agent's id as `audience`, a subset of scopes, and a `ttl_seconds`. Save the new `token` and hand it to that agent. 3. At an access checkpoint, POST /authorize with the `token`, the exact `scope` the action needs, and the `presenter` id. Proceed only if `authorized:true`. (Or use POST /verify if you just want the token's full scope list and validity.) 4. To audit a token before trusting it, POST /inspect to see its entire delegation chain — who granted what to whom, and whether any hop is revoked or expired. 5. To withdraw access, POST /revoke with the token you want to kill. That token and every token delegated from it immediately fail /authorize and /verify. 6. If the first call is slow, the free host may be waking up — call GET / once to warm it, then retry.