feat(api): GitHub App installation tokens join the token pool - #315
Conversation
Adds App installation tokens as first-class slots ahead of the env PATs, so the hosted service can run on App quota and the personal PAT can retire from the pool (#308 follow-up). - api/utils/github-app-token.ts mints installation tokens from GH_APP_ID + GH_APP_PRIVATE_KEY (PEM verbatim, \n-escaped, or base64) with a hand-rolled RS256 JWT via Node crypto — no new dependency. Tokens cache in module memory per instance, refresh 5 minutes before expiry, and concurrent callers share one in-flight mint (~2 REST calls per installation per instance per hour). Never logged, never in Redis. - One slot PER INSTALLATION: GH_APP_INSTALLATION_IDS (comma-separated) pins the installations; each has its own independent 5,000-point hourly GraphQL quota, so installing the App on both backing accounts yields two pools from one credential. Unset, the first discovered installation is used. - The rotation acquires tokens inside the retry loop: a failed mint is flagged isTokenAcquisition and rotates to the next slot exactly like a rate-limited PAT — a broken App degrades to PAT service, not to error cards. Slot names (GITHUB_APP_n / GITHUB_TOKEN_n) keep logs readable without exposing accounts. Rollout: create the App (no extra permissions; default read-only metadata is enough — cards read public data only), install it on the backing accounts, set the three env vars, deploy, then delete the personal GITHUB_TOKEN env and rename GITHUB_TOKEN_1 to GITHUB_TOKEN. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds GitHub App installation token minting with caching and concurrency control, combines App and PAT credentials into unified slots, and updates card rendering to acquire and rotate tokens per retry attempt. ChangesGitHub App token lifecycle
Unified token slots
Card retry integration
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant handleCard
participant github-token-updater
participant github-app-token
participant GitHub
handleCard->>github-token-updater: request token for current slot
github-token-updater->>github-app-token: mint or reuse App token
github-app-token->>GitHub: resolve installation and mint token
GitHub-->>github-app-token: installation token
github-app-token-->>github-token-updater: token or acquisition failure
github-token-updater-->>handleCard: token or acquisition failure
handleCard->>handleCard: rotate to next slot on failure
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@api/utils/github-app-token.ts`:
- Around line 71-81: Update appApi to include an explicit axios timeout in its
request configuration. Use the project’s established timeout constant or
configuration value if available, while preserving the existing method, URL, and
headers for installation discovery and token minting.
In `@tests/utils/handle-card.test.ts`:
- Around line 145-169: Update the `handleCard with a GitHub App slot` test setup
to declare the axios mock as a mutable `let` and instantiate `new
MockAdapter(axios)` inside `beforeEach()` before resetting it, ensuring each
test starts with an attached adapter. Keep `afterEach()` responsible for
restoring the per-test mock.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: eb7b579a-904f-4953-9178-68e58fe0aef4
📒 Files selected for processing (6)
api/utils/github-app-token.tsapi/utils/github-token-updater.tsapi/utils/handle-card.tstests/utils/github-app-token.test.tstests/utils/github-token-updater.test.tstests/utils/handle-card.test.ts
Review feedback: appApi now carries an explicit 10s axios timeout so a hanging GitHub API call degrades to the PAT slots within the request instead of riding the invocation to the platform kill. The handle-card App-slot tests recreate the MockAdapter per test — restore() detaches the adapter, so a suite-level instance left later tests unmocked. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Both review points addressed in 7207986: |
What
App installation tokens become first-class token-pool slots, ahead of the env PATs. This lets the hosted service run on App quota so the personal PAT can retire from the pool (#308 follow-up).
api/utils/github-app-token.tsmints installation tokens fromGH_APP_ID+GH_APP_PRIVATE_KEY(PEM verbatim,\n-escaped, or base64) with a hand-rolled RS256 JWT via Nodecrypto— no new dependency. Tokens cache in module memory per instance, refresh 5 minutes before expiry, and concurrent callers share one in-flight mint (~2 REST calls per installation per instance per hour). Never logged, never written to Redis.GH_APP_INSTALLATION_IDS(comma-separated) pins them. Each installation carries its own independent 5,000-point hourly GraphQL quota — installing the App on both backing accounts yields two pools from a single credential. Unset, the first discovered installation is used.isTokenAcquisitionand rotates to the next slot exactly like a rate-limited PAT. A broken App degrades to PAT service, not to error cards. Username-hash spread now coversappSlots + patSlots.GITHUB_APP/GITHUB_APP_<n>/GITHUB_TOKEN_<n>— never the accounts behind them.Compatibility note
The queries are already integration-token-clean: #313 replaced the
stargazersconnection (blocked for App/Actions tokens by GitHub's July change) withstargazerCount, and the repro verified the full profile document works with an integration token. New query fields should be probed with an Actions token before shipping.Rollout
GH_APP_ID,GH_APP_PRIVATE_KEY(mark sensitive),GH_APP_INSTALLATION_IDS=<id1>,<id2>.Using token source: GITHUB_APP_0/1.GITHUB_TOKENenv; renameGITHUB_TOKEN_1→GITHUB_TOKEN. Pool becomes App×2 + machine PAT ≈ 15k points/hr, personal account fully off the hot path.Testing
27 suites / 241 tests green, typecheck + lint clean. New coverage: JWT signature verified against a real throwaway RSA keypair (alg, iss, exp≤10min), token caching + refresh-margin re-mint + concurrent-mint coalescing, per-installation isolation, base64/escaped PEM handling, slot arithmetic with and without the App, and handle-card rotation when the mint fails (degrades to PATs; App slot counts in the exhaustion lap).
🤖 Generated with Claude Code
Summary by CodeRabbit