Skip to content

gh-wrapper: cache the GH_TOKEN identity lookup instead of one gh api user call per invocation #303

Description

@twistedmelonman

Context

PR #302 added a fail-closed guard to _gh_wrapper_sync_identity for the case where GH_TOKEN overrides the keyring identity that gh auth switch selects. That guard needs to know which identity the token authenticates as.

It resolves that in two ways:

  1. CLAUDE_GH_TOKEN_LOGIN, if set — the test fixtures set this directly.
  2. Otherwise, a gh api user --jq .login call.

In production, CLAUDE_GH_TOKEN_LOGIN is unset, so path 2 is what runs.

The problem

_gh_wrapper_sync_identity runs on every gh invocation. So every gh command now costs one extra network round trip to resolve an identity that does not change within a session.

This was called out as a known follow-up when the guard was written and deliberately left unbuilt, to keep the change to the cheap tier.

Rough shape of a fix

Cache the resolved login for the life of the shell session. The token itself is fixed per session (exported once by claude-wrapper/lib/credentials.sh), so the resolved login cannot drift underneath the cache while the session lives.

Worth deciding as part of the fix:

  • Cache in a shell variable versus a file under ${TMPDIR}. A shell variable dies with the session, which matches the token's lifetime exactly; a file needs an invalidation story.
  • Whether the cache key should incorporate the token value, so a mid-session GH_TOKEN change is not served a stale identity. Cheap insurance, since the comparison is what the guard's correctness rests on.
  • Failure behavior on a cache miss when the network is down. The guard currently fails closed when it cannot resolve the login, which is the right default — caching must not quietly turn that into a fail-open.

Not in scope

The full per-org token router (CLAUDE_GH_TOKEN_ROUTER) stays deferred. It needs a second PAT for andrewmrich and reverses a documented product decision in claude-wrapper/README.md.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions