Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLI
Assigneesstack72

Relationships

#1511 Move per-login token secrets from vault to encrypted control-plane store

Opened by stack72 · 8/3/2026· Shipped 8/4/2026

Problem

swamp serve stores per-login bearer token secrets in the vault under dynamically-named keys (server-token-oauth-<random>, oauth-access-token-oauth-<random>). This works fine with local_encryption but breaks badly with external vault backends like @swamp/aws-sm:

  • IAM: Key names are random and unprefixable — can't be covered by a scoped IAM policy without wildcards + CreateSecret
  • Cost: $0.40/secret/month, 2 secrets per login, no cleanup on revoke/expire
  • Litter: SM's 7-30 day forced deletion window means expired/revoked tokens can't be cleaned up immediately
  • Throttling: SM write rates are a poor fit for session-store patterns

Additionally, revoke() in server_token_model.ts only flips the data record state — it never calls vaultService.delete() on either key, so secrets accumulate indefinitely regardless of backend.

Proposed approach

Move token secrets out of the vault and into the control-plane store, encrypted at rest:

  1. Encryption: Use the same AES-256-GCM + PBKDF2 primitives from local_encryption_vault_provider.ts. The encryption key is internal cluster coordination state, not a customer-managed secret — a validated serve instance self-bootstraps it.

  2. Key bootstrap: First instance generates the key and stores it via controlPlaneStore.putIfAbsent("token-encryption-key", ...). Subsequent instances read it on startup. Same race-safe pattern local_encryption already uses for auto-generated keys.

  3. Storage: Token secrets stored as encrypted blobs in the control-plane store under a token-secrets/ prefix. delete() is immediate — no recovery window.

  4. Cleanup: revoke() and the expiry path should delete the corresponding secrets, not just flip state.

This keeps the vault for what it's good at — durable infrastructure credentials (OAuth client ID/secret, bootstrap token, resolved admins) — and moves session-grade material to the store that already handles cross-instance coordination.

Affected files

  • src/serve/device_auth_handler.tsmintServerTokenImpl, storeAccessToken
  • src/domain/models/access/server_token_model.tsrevoke, SERVER_TOKEN_SECRET_KEY_PREFIX
  • src/domain/models/access/server_token_model.tsredeem (reads the secret for verification)
  • src/serve/collective_refresh_service.ts — reads oauth-access-token-*
  • src/serve/capability_service.tsDENIED_SECRET_KEY_PREFIXES guard (may no longer be needed for these keys)

Context

Reported by a customer running self-hosted swamp serve on EKS with @swamp/aws-sm vault backend. Their IAM policy covers the documented fixed keys but not the dynamic server-token-oauth-* pattern, and widening with wildcards + CreateSecret conflicts with their governance standards.


Automoved by swampadmin from https://github.com/swamp-club/swamp/issues/2051

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 4 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

8/4/2026, 6:44:06 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack728/3/2026, 10:59:34 PM

Sign in to post a ripple.