Repository Public MIT
1# ADR-0005 — API token design23Status: accepted, 2026-11-09.45## Format67`skr_<prefix>_<secret>`89- `prefix`: 10 random characters from `a-z0-9`. Public identifier, stored in clear, shown in listings and audit entries.10- `secret`: 32 bytes from the operating system CSPRNG, base64url (43 characters, 256 bits).1112The fixed `skr_` shape lets secret scanners, including the registry's own, recognise a leaked token.1314## Storage1516Only `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.1718The 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.1920## Grants2122- **Scopes**: listed in `packages/registry-core/src/constants.ts`. A route declares at most one required scope; services re-check.23- **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.24- **Expiry**: mandatory. Default 90 days, maximum 366 days.25- **IP allowlist**: optional, up to 16 IPv4 addresses, IPv4 CIDR ranges or exact IPv6 addresses.26- **Quota**: 50 active tokens per user.2728## Elevation2930`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.3132## No escalation3334A token with `tokens:write` can create tokens only with a subset of its own scopes and namespaces, never elevated, and never outliving itself.3536## Rotation and revocation3738- 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.39- A token may always rotate or revoke itself. Acting on another token needs `tokens:write`.40- 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.4142## Consequences4344- 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.45- 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.46- Losing `TOKEN_PEPPER` invalidates every token and session; rotating it is the emergency "revoke everything" switch.47