Skip to content

feat: cache JWT on disk to respect issuance rate limit#4

Open
adamkrim wants to merge 1 commit into
mainfrom
feat/jwt-cache
Open

feat: cache JWT on disk to respect issuance rate limit#4
adamkrim wants to merge 1 commit into
mainfrom
feat/jwt-cache

Conversation

@adamkrim

Copy link
Copy Markdown
Collaborator

Problem

Each CLI invocation (e.g. a Terraform command) is a new process that mints a fresh JWT via checkAPIToken. A few commands per hour exceed the API's JWT issuance rate limit (15/hour per user and per IP).

Solution

Persist the issued JWT and reuse it until it nears expiry, so we only call checkAPIToken when there's no valid token.

  • Storage: user-private file under os.UserCacheDir()/elestio/, mode 0600, filename = sha256(email:apiKey) so accounts don't collide and raw credentials never hit disk.
  • Expiry: read from the token's own exp claim (golang-jwt/jwt/v5, ParseUnverified — the server stays the authority on validity). Reused only while >5 min from expiry.
  • Self-healing: on a 401 the cache is dropped and we sign in once more, so a server-revoked token recovers automatically (the auth endpoint itself is excluded to avoid a loop).
  • Escape hatch: ELESTIO_DISABLE_JWT_CACHE=1 disables caching entirely.

Caching is best-effort: any cache read/write error falls back to a normal sign-in.

Tests

cache_test.go covers exp parsing, round-trip, per-credential isolation, the expiry margin, deletion, and the disable flag. go build, go vet, and tests pass.

Notes for reviewers / security

  • Bearer token on disk is user-private (0600), shorter-lived than the API key already stored in env/config, and never written to Terraform state or the repo.
  • Please confirm the JWT lifetime so we can validate the 5-min refresh margin.

🤖 Generated with Claude Code

Each CLI invocation (e.g. a Terraform command) was a new process that
minted a fresh JWT via checkAPIToken, quickly exceeding the API's JWT
issuance rate limit (15/hour per user and per IP).

Persist the issued JWT to a user-private file (os.UserCacheDir, mode
0600, keyed by a hash of the credentials) and reuse it until it nears
expiry (5 min margin, read from the token's exp claim). On a 401 the
cache is dropped and we sign in once more, so a server-revoked token
self-heals. Set ELESTIO_DISABLE_JWT_CACHE=1 to opt out.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant