Secrets and tokens
Decisions on token kinds, secret storage and key rotation.
ADR-010: Token kinds pat, svc and cst; PATs span projects, service tokens are CI-only and bound to one project
Status: Accepted, 2026-10-15
Context
One opaque token must work for Composer HTTP Basic and npm Bearer across npm, pnpm and yarn Berry; a token prototype ran 64 client cases per runtime with identical results on Node and workerd. Composer stores credentials per host, so a developer needs one credential for all projects on the broker host. A PAT therefore spans many projects and a service token is CI-only. Expiry defaults and the validity of each token kind are set below.
Decision
- Token format is
pb_(pat|svc|cst)_<selector>_<secret>. The prefix encodes the principal kind only. Capability lives in scopes. The stored form is a selector plus HMAC-SHA-256 with a versioned pepper (ADR-012). - Scopes are
read(install),write(publish, deprecate, dist-tag; P1) andfull(API operations the user may do). Effective access is requested scopes intersected with the principal's grants, the creator's role and the project token policy. A request for a scope that is not allowed is refused, not narrowed. pat: created by a user; limited to selected projects or all of the user's projects; accepted on every project URL within its scope, so one Composerhttp-basicentry per host covers all projects.svc: service principal owned by a user, CI only, bound to one project (organization-wide only for organization admins). It stays valid until it expires or is revoked, regardless of the creator's later membership. The audit log records the creator.cst: its own principal type because a customer has no owning user;readonly; expiry per the licence-freeze rules (ADR-018).- Default maximum expiry is 90 days; CI read-only service tokens may last one year.
- PATs and user sessions are re-validated against the identity provider daily and at every login.
- Every request checks the grant, so a token never outlives a revoked grant;
last_used_atis written lazily; revocation is immediate. - The vendor API key is a
svctoken withcustomers:*scopes.
Consequences
- Positive: one credential per laptop; CI uses
COMPOSER_AUTHwith a project token; the format works unchanged on both runtimes and all supported clients (yarn needsnpmAlwaysAuth). - Negative: a service token outlives its creator's membership, which is a deliberate choice made visible through the audit record; token lookup and per-request grant checks add one indexed query.
- Follow-ups: GitHub OIDC exchange for short-lived publish tokens; the Tokens screen shows kind and scopes in separate columns.
ADR-012: Secrets and cryptography with a deployment master key, HKDF, record-bound AAD and a versioned pepper
Status: Accepted, 2026-10-15
Context
Source credentials, GitHub App keys and webhook secrets are stored by the broker and must survive key rotation without downtime. With one organization per deployment (ADR-002), keys are scoped per deployment, not per organization.
Decision
- The deployment holds one 32-byte random master key in the environment or a binding (named "master key", not "salt").
- Credential ciphertexts use AES-256-GCM with a random 96-bit nonce. Keys are derived with HKDF with a
key_version, and the additional authenticated data is bound to the record, so a ciphertext cannot be moved to another record. - Token secrets are stored as selector plus HMAC-SHA-256 under a versioned pepper (ADR-010).
- Rotation loads two key versions at once. A command and job re-encrypt every ciphertext under a new
key_versionwith progress and resume, rotate the pepper version, and report what remains on the old version, without downtime. - Per-deployment keys and blob prefixes apply; deduplication happens within one deployment.
Consequences
- Positive: rotation without downtime; a stolen database dump without the master key does not reveal credentials; a ciphertext cannot be replayed onto another record.
- Negative: losing the master key makes credentials unrecoverable, so the key belongs in the backup process of the operator (ADR-019); two keys loaded during rotation widen the exposure window briefly.
- Follow-ups: backups include ciphertexts and keys together with the stated handling; rotation runbook in the deployment documentation.