Relationships
#1389 A collective token stored in auth.json (rather than SWAMP_API_KEY) is permanently scope-less — resolution only runs for the env var, and scopes are never persisted for collective tokens
Opened by magistr · 7/24/2026
Summary
Startup scope resolution runs only when SWAMP_API_KEY is present in the environment. A collective token that lives in auth.json instead therefore never has its scopes resolved, and — because nothing ever writes scopes into auth.json for a collective token — the fallback path finds nothing either. The result is a credential that is permanently and unrecoverably scope-less: every team-feature gate throws "lacks the X scope", forever, no matter what the token actually holds.
Where (source)
src/cli/mod.ts:1337— the whole resolution block is gated on[HOST-1]("SWAMP_API_KEY"):
This is the only placeif (!hookMode && [HOST-1]("SWAMP_API_KEY")) { ... }loadScopeCache/whoami/setAuthScopes(response.scopes)are reached.src/cli/mod.ts:1389-1392— the non-env fallback readscreds?.scopesout ofauth.json.- But that field is never populated for a collective token:
src/libswamp/auth/whoami.ts:141short-circuits onif (!response.collectiveToken), so collective tokens skip the credential save entirely.src/libswamp/auth/whoami.ts:91-102—createAuthDeps.saveCredentialscallssaveIdentityCache(serverUrl, username, collectives, fingerprint)with noscopesargument, andauth_repository.ts:170-178rebuilds the record from scratch with...(scopes ? {scopes} : {}), dropping any that were there.
src/cli/auth_context.ts:53-55—setCollectiveTokenstill fires (it is a local prefix test on the stored key), so the credential is classified as collective while its scopes stayundefined.src/cli/auth_context.ts:85-98— collective + undefined scopes is exactly the throw condition.
Deterministic repro
export SWAMP_HOME="$(mktemp -d)"; mkdir -p "$SWAMP_HOME/config"Write an auth.json whose apiKey is a collective token (a value beginning with the org prefix), with no scopes field — which is the only shape the CLI itself ever produces:
{"serverUrl":"https://swamp-club.com","apiKey":"swamp_org_...","apiKeyId":"id",
"username":"repro","collectives":[],"apiKeyFingerprint":"swamp_org_gg"}Then, with SWAMP_API_KEY not set:
swamp vault create @swamp/aws-sm v1Observed:
Error: Your collective token lacks the vault:* scope.No whoami is attempted, no cache is consulted, and there is no sequence of commands that populates scopes for this credential. The suggested remedy in the error ("create a collective token ... and set SWAMP_API_KEY") is, as it happens, the only thing that works — but the message presents it as a fix for a token that lacks a scope, not as "this credential must be delivered via the environment variable."
Impact
Two ways in:
- A user who puts their collective token into
auth.jsonby hand (reasonable — it is where credentials live, andservers.json/auth.jsonare otherwise the CLI's own storage) gets a credential that fails every team feature with a message about scopes. - Any future code path that persists a collective token to
auth.jsoninherits the same dead state.
The failure is silent about its real cause. The token is fine, the scopes are fine, the delivery mechanism is the problem — and nothing in the error mentions the delivery mechanism.
Expected
- Run scope resolution against whatever credential is in effect, not only against the environment variable — i.e. lift the
SWAMP_API_KEYcondition atmod.ts:1337and resolve forcreds.apiKeyregardless of source. - Persist
scopesfor collective tokens too, or state plainly that collective tokens are env-var-only and say so in the error when one is found inauth.json.
Related
- #1381, #1382, #1384 — three other routes to the same misleading "lacks the X scope" string. Consider making that message name its cause in all four cases.
Environment
- Verified against source
main@10943ef. Gate shipped ina519305(#1934), 2026-07-23; installed stable20260722.210030.0-sha.7ff1e2f0predates it. - macOS 14.
Closed
No activity in this phase yet.
stack72 commented 7/24/2026, 9:48:56 PM
This is correct - a token stored in auth.json is a personal access token and has no scopes at this time! This is by design
magistr commented 7/24/2026, 9:58:14 PM
Author follow-up after checking the manual, in fairness to triage: the documented path for a collective token is the environment variable only. /manual/how-to/authenticate-with-api-keys says to 'Export a collective token (prefixed swamp_org_) as SWAMP_API_KEY', and neither that page nor /manual/reference/api-key-authentication suggests placing one in auth.json.
So this is an undocumented usage rather than a documented-and-broken one, which lowers the severity from what I filed. What remains worth fixing is the diagnosis, not the capability: the CLI accepts the credential, classifies it as a collective token via the local prefix test, and then reports 'lacks the vault:* scope' — a statement about scopes for what is really a delivery-mechanism problem. Either resolve scopes for whatever credential is in effect regardless of source, or detect a swamp_org_-prefixed key in auth.json and say plainly that collective tokens must be supplied via SWAMP_API_KEY.
Note also that /manual/reference/api-key-authentication documents auth.json as the credential store without mentioning that a collective token placed there behaves differently, which is what makes the trap easy to walk into.
Sign in to post a ripple.