ADR-0005 — API token design
Status: accepted, 2026-11-09.
Format
skr_<prefix>_<secret>
prefix: 10 random characters froma-z0-9. Public identifier, stored in clear, shown in listings and audit entries.secret: 32 bytes from the operating system CSPRNG, base64url (43 characters, 256 bits).
The fixed skr_ shape lets secret scanners, including the registry's own, recognise a leaked token.
Storage
Only HMAC-SHA-256(TOKEN_PEPPER, "api-token\0" + token) and the prefix are stored. With 256 bits of entropy in the secret, a slow password hash adds nothing; the keyed HMAC means a database dump alone cannot be used to verify guesses. Comparison is constant-time. The pepper lives only in the server environment.
The full token is returned once by the creating call (HTTP response with Cache-Control: no-store, or stdout of the server console) and cannot be retrieved again.
Grants
- Scopes: listed in
packages/registry-core/src/constants.ts. A route declares at most one required scope; services re-check. - Namespaces: a token lists the namespaces it may write to. Publishing scopes require at least one. The issuing user must be able to publish to each.
- Expiry: mandatory. Default 90 days, maximum 366 days.
- IP allowlist: optional, up to 16 IPv4 addresses, IPv4 CIDR ranges or exact IPv6 addresses.
- Quota: 50 active tokens per user.
Elevation
admin:moderate and admin:settings are elevated scopes. A token holding one is flagged elevated, can be issued only to a platform owner or administrator, only from the server console or a session re-authenticated within 15 minutes, and expires within 24 hours. A token cannot create or rotate an elevated token.
No escalation
A token with tokens:write can create tokens only with a subset of its own scopes and namespaces, never elevated, and never outliving itself.
Rotation and revocation
- Rotation creates a replacement with the same name, scopes, namespaces, allowlist and original lifetime, links it with
rotated_from, and revokes the old token in the same transaction. - A token may always rotate or revoke itself. Acting on another token needs
tokens:write. - Revoked, expired, unknown and malformed tokens all receive HTTP 401; the precise reason is in the error code (
token_revoked,token_expired,invalid_token) and in the audit log.
Consequences
- The default Claude Code publisher token (
PUBLISHER_SCOPES) can read, create, update, upload and publish in its namespaces. It cannot revoke releases, deprecate artifacts, list tokens, read the audit trail or moderate. - Self-rotation lets a leaked token extend its own life, but it also revokes the copy the owner holds, which surfaces the theft on the owner's next call.
- Losing
TOKEN_PEPPERinvalidates every token and session; rotating it is the emergency "revoke everything" switch.