Skip to main content
← Back to list
01Issue
BugIn ProgressSwamp CLI
Assigneesstack72

Relationships

#1616 Server token definitions and encryption keys are not durable across pod restarts

Opened by stack72 · 8/12/2026

Description

When swamp serve mints a server token (via OAuth login or access token mint), the token state is split across three stores with different durability:

  • Definition YAML → local filesystem (.swamp/auto-definitions/swamp/server-token/<id>.yaml)
  • Token data record → shared datastore (GCS)
  • Token secret → shared control plane vault (_token-secrets in GCS)

If the pod is replaced before the definition file syncs to GCS — or if sync never happens — the definition is lost. The secret and data remain in GCS but can never be used because authenticateServerToken runs modelMethodRun which requires the definition to exist. The token is permanently broken.

Encryption key durability

The control plane vault encrypts all token secrets with an AES-GCM key stored at token-secrets/encryption-key in the control plane store. In a shared store (GCS), the key is written once via putIfAbsent and shared across pods. However, if the key is lost — GCS bucket wipe, namespace/prefix change, fallback to local filesystem — a new key is generated and all existing token secrets become undecryptable, even if the definitions and data survive.

Failure modes:

  • Namespace/prefix change between deployments — old key is invisible under the new prefix, new key generated, all existing tokens broken
  • putIfAbsent bug in the datastore extension — if the implementation doesn't use proper preconditions, concurrent pods could write different keys
  • Fallback to local filesystem — if the remote control plane store isn't available at startup, FileSystemControlPlaneStore is used and the key is ephemeral

Diagnostic: check server startup logs for "Generated new token encryption key" (new key — potential problem) vs "Loaded existing token encryption key" (existing key found — correct).

Impact

  • Users or workers with the affected token get "Authentication failed" indefinitely
  • The failing client retries, which can trigger the per-IP rate limiter and block other legitimate clients on the same IP (#1615)
  • Orphaned secrets and data accumulate in the control plane store with no corresponding definition, invisible to any scan or GC (#1614)
  • No error or warning at mint time — the operation appears to succeed

Observed in production

A worker token platform-fleet-gke-svc was minted on a previous pod incarnation. After pod replacement, the definition was lost. The worker retried auth every second, producing "Model not found: platform-fleet-gke-svc" on every attempt, burning through the rate limit budget and blocking all other clients on the same cluster IP.

Proposed Fix

Definition durability

The mint operation should be atomic — the definition must land in the durable shared store before the token is returned to the client. Options:

  1. Store definitions in the shared datastore — write the definition to GCS alongside the data and secret, not to local YAML. The definition repo should be able to read from the shared store.
  2. Sync before returning — ensure syncService.pushChanged() completes and includes the definition file before returning the token to the caller. Today the definition is written via a local YamlDefinitionRepository that may not be covered by the sync.
  3. Verify round-trip — after mint, verify the definition can be read back via the same code path that authenticateServerToken uses.

Encryption key durability

  • Log a warning at startup if a new encryption key is generated on a server with a remote control plane store — this likely means the old key was lost
  • Consider storing the encryption key path outside the namespaced prefix so it survives namespace changes
  • Verify putIfAbsent implementations in datastore extensions use proper conditional writes (GCS: ifGenerationMatch=0, S3: If-None-Match)

Orphan cleanup

A sweep at startup should detect orphaned secrets in _token-secrets that have no corresponding definition, log them, and optionally clean them up.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 7 MOREREVIEW+ 3 MOREPR_LINKED

In Progress

8/12/2026, 4:20:44 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack728/12/2026, 3:15:17 PM

Sign in to post a ripple.