Relationships
#2191 Expired enrollment token cannot be re-minted unless a worker was connected when it expired
Opened by keeb · 9/16/2026· Shipped 9/17/2026
Summary
An enrollment token whose expiresAt has passed cannot be re-minted under the same name unless a worker happened to be connected at the moment it expired. The fix for re-minting after revoke/expire (ae1ed149, related to #2078) keys off the stored state, but the stored state only becomes expired when the orchestrator's expiry timer fires for a connected worker. A token that was never enrolled, or whose workers were disconnected at expiry, stays unused/enrolled forever and worker token create <same-name> fails with "already exists — revoke it or wait for expiry before re-minting" — waiting does not help.
This bites automated rotation (e.g. a Kubernetes CronJob that re-mints a fixed-name fleet token into a Secret): if the fleet was scaled to zero when the token lapsed, the rotator wedges.
Reproduction
swamp worker token create t1 --duration 2m --server ...- Do not connect any worker. Wait >2m.
swamp worker token create t1 --duration 2m --server ...→ "Enrollment token 't1' already exists — revoke it or wait for expiry before re-minting".
Observed on 20260911.215321 (d18f2d86); the mint guard is unchanged at 34d98319.
Where
src/domain/models/worker/enrollment_token_model.tsmint: rejects unless the storedstateisrevokedorexpired; it does not compareexpiresAtto now.expireis only invoked fromsrc/serve/worker_gateway.ts#scheduleTokenExpiry, which is scheduled at enrollment of a connected worker.
Fix approach
Treat a token whose expiresAt is in the past as expired in the mint guard (derived state), independent of whether the expire transition was ever recorded. Consider applying the same derived-state view in worker token list so the listed state matches reality. Add a model test for re-minting a never-enrolled token past its lifetime.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.