Skip to main content
← Back to list
01Issue
BugClosedSwamp CLITeam
AssigneesNone

Relationships

#1384 Collective-token scope cache never expires, is never refreshed by whoami, and cannot be cleared via the CLI — scope changes and revocations never take effect

Opened by magistr · 7/24/2026

Summary

Once a collective token's scopes are cached, they are used forever and there is no supported way to refresh or remove them:

  1. scope_cache.json has no TTL, no timestamp, and no version field. On a cache hit the live whoami is skipped entirely.
  2. Changing a token's scopes on swamp-club.com without rotating the token therefore never takes effect on any machine that has already run once.
  3. swamp auth whoami reads scopes live from the server and never writes the cache, so it cheerfully reports scopes that contradict what the gate is enforcing one line later.
  4. swamp auth logout — the only command that removes the file — refuses to run while SWAMP_API_KEY is set, and early-returns when no auth.json exists. For a collective-token-only user (CI, headless), both conditions hold, so the file is unreachable through the CLI.

Where (source)

  • src/infrastructure/persistence/auth_repository.ts:186-214saveScopeCache writes {fingerprint, scopes}; loadScopeCache validates on fingerprint alone. No expiry anywhere.
  • src/cli/mod.ts:1345-1348 — cache hit short-circuits the whoami.
  • src/libswamp/auth/whoami.ts:141-148 — whoami has the fresh scopes in hand and does not call saveScopeCache. Grep confirms saveScopeCache has exactly one caller: mod.ts:1356.
  • src/cli/commands/auth_logout.ts:42-47 — hard refusal when SWAMP_API_KEY is set.
  • src/libswamp/auth/logout.ts:82-91 — returns "not authenticated" before reaching deleteCredentials() when load() yields nothing.
  • src/infrastructure/persistence/auth_repository.ts:217-227delete() does remove scope_cache.json, but is unreachable in the above case.

Deterministic repro

A — the cache cannot be cleared

export SWAMP_HOME="$(mktemp -d)"; mkdir -p "$SWAMP_HOME/config"
printf '{"fingerprint":"swamp_org_ab","scopes":["vault:*"]}\n' \
  > "$SWAMP_HOME/config/scope_cache.json"

SWAMP_API_KEY=[REDACTED-SECRET-1] collective token> swamp auth logout
# -> Error: Authenticated via SWAMP_API_KEY environment variable. Unset it to log out.

swamp auth logout          # env var unset; no auth.json exists (the CI case)
# -> Not currently authenticated.

ls "$SWAMP_HOME/config/scope_cache.json"    # still there

Both doors are shut: the first because the env var is set, the second because there are no credentials to delete. The stale scopes survive.

B — whoami and the gate disagree

Same SWAMP_HOME, with the cache above and an unreachable SWAMP_CLUB_URL:

swamp vault create @swamp/aws-sm v1
# -> passes the vault:* gate (proceeds to an unrelated downstream error),
#    entirely from cache, with no network reachable

swamp auth whoami
# -> Error: Request to <swamp-club>/api/whoami timed out after 15s.

The gate answers offline from a file; whoami only ever asks the server. With the network up, the same split produces the worse version: whoami prints Scopes: datastore:* while the very next command reports "Your collective token lacks the datastore:* scope."

Impact

This is the recovery path for every scope error, and it is a dead end. A user who hits "lacks the X scope", adds X to the token in the web UI, and re-runs gets the identical error forever. The diagnostic they will reach for — swamp auth whoami — actively confirms the token does have the scope, which reads as a CLI bug rather than a stale cache. The only remedy is rm ~/.config/swamp/scope_cache.json, which is documented nowhere.

It also means scope revocation never propagates: narrowing or removing a scope has no effect on any machine that cached before the change, for as long as the token itself is not rotated.

Expected

  • Give the cache a TTL (and/or store the token's updatedAt from the server) so scope changes converge without rotation.
  • Have swamp auth whoami write through to the cache — it already has authoritative scopes in hand, and it is the natural "refresh" gesture.
  • Make the cache clearable: either let swamp auth logout remove it even when SWAMP_API_KEY is set / no auth.json exists, or add an explicit swamp auth cache clear / --refresh.
  • When rendering scopes in whoami, distinguish live from cached so the two can be compared.
  • #1381 — unresolved scopes rendered as "lacks the scope".
  • #1382 — the cache key itself collides across collective tokens.

Environment

  • Verified against source main @ 10943ef. The gate shipped in a519305 (#1934), 2026-07-23; installed stable 20260722.210030.0-sha.7ff1e2f0 predates it.
  • macOS 14.
02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED

Closed

7/24/2026, 10:45:04 PM

No activity in this phase yet.

03Sludge Pulse
Editable. Press Enter to edit.

magistr commented 7/24/2026, 9:58:21 PM

Author follow-up: I checked the published manual, and it strengthens rather than weakens this report.

/manual/reference/api-key-authentication documents credential storage as auth.json with mode 0600 and makes no mention of scope_cache.json. Neither /manual/explanation/api-key-scoping nor /manual/how-to/authenticate-with-api-keys refers to scope caching, cache lifetime, or any way to refresh or clear it.

So the file that actually decides whether a team-feature command succeeds is absent from the documentation entirely. A user whose token was updated on the website has no documented reason to suspect a local cache exists, no documented command to clear it, and — per the body of this issue — no undocumented one either, since logout is unreachable in the collective-token case. Whatever TTL or refresh mechanism gets chosen, the file is worth naming in the credential-storage reference alongside auth.json.

stack72 commented 7/24/2026, 10:45:04 PM

@magistr Thanks for the detailed reports — we appreciate the work you're putting in to make the tool better. Fixes are in #1956 and will be released shortly.

Sign in to post a ripple.