Add client-side API token auto-refresh (GL-1709)#444
Closed
timmarkhuff wants to merge 3 commits into
Closed
Conversation
Treat the supplied API token as a bootstrap credential: mint a short-lived working token, cache it per-token on disk, and rotate it on a background daemon thread so the bootstrap token is never used directly for API calls. Includes a 401 re-mint fallback, context-manager support, and unit tests. Gated opt-in for now via auto_refresh_token / GROUNDLIGHT_AUTO_REFRESH_TOKEN until the server-side token-management endpoints are live everywhere. Co-authored-by: Cursor <cursoragent@cursor.com>
The auto-refresh work added a close() lifecycle method to Groundlight, which the CLI auto-registers as a command and looks up in _COMMAND_GROUPS, causing KeyError: 'close'. Exclude it like other non-shell methods. Co-authored-by: Cursor <cursoragent@cursor.com>
A due-but-failing rotation left the token overdue, so the next wait was 0s and the refresh loop would spin and hammer the API during an outage. Wait a fixed backoff after a failed cycle instead. Adds a test for the loop's failure path. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Treats the API token supplied to
Groundlight(...)(orGROUNDLIGHT_API_TOKEN) as a bootstrap credential rather than the credential used on every request. The SDK mints a short-lived working token from it, caches that token on disk, and rotates it on a background thread, so a long-lived bootstrap token is never sent on normal API calls. This limits the blast radius of a leaked token and lets the server enforce a bounded token TTL without breaking long-running integrations.Design doc: GL-1709 plan (
docs/gl-1709-api-token-security-plan.mdin zuuul). Server-side endpoints land in zuuul PR #6579.What's included
src/groundlight/token_manager.py(new) - the auto-refresh engine:~/.groundlight/tokens/<snippet>.json(mode0600, dir0700), overridable viaGROUNDLIGHT_TOKEN_DIR.filelock) with atomic slot writes (temp +os.replace).sdk-auto xxxxxx.gl-token-refresh-<snippet>) rotates once per day with double-checked locking, and deletes the previous token after a 2x grace window so tokens don't accumulate.client.py): wires the manager intoGroundlight.__init__, addsclose()and context-manager support (with Groundlight() as gl:).internalapi.py:GroundlightApiClient.call_apire-mints and retries once onUnauthorizedException.config.py: named constants (TOKEN_TTL_DAYS=30,REFRESH_INTERVAL_DAYS=1,CLEANUP_GRACE_FACTOR=2) and env-var names.spec/public-api.yamlto add theApiTokensApi(create/list/delete/get-by-snippet) and related models.filelockpromoted to a direct dependency (Unlicense).test/unit/test_token_manager.py(21 tests, no network) covering naming, cold start, caching, expiry, rotation, cleanup, 401 re-mint, and TTL.Decisions to confirm before merge
auto_refresh_token=/GROUNDLIGHT_AUTO_REFRESH_TOKEN(default off). The GL-1709 plan calls for always-on, but with it always-onGroundlight()mints on every startup, which 404s wherever the server endpoints aren't deployed yet (and breaks the SDK's own test suite). Flipping the default to on is a one-line change once the server side is live. MarkedTODO(GL-1709).platformdirs. Its current release requires Python 3.10 but the SDK supports 3.9. Usedpathlib.Path.home()for the token dir instead (Linux is the priority per the plan).Blockers gated by
TODO(GL-1709)GET /v1/api-tokens/by-snippet/{snippet}endpoint. The replacement method (_get_token_name_by_snippet) is already written but not called, since that endpoint isn't live yet.Test plan
pytest test/unit/test_token_manager.py(21 passing, no network)