Skip to content

Cloudflare Git Token Service - Add new worker for GitHub token management#19

Open
eshurakov wants to merge 1 commit intomainfrom
eshurakov/cloudflare-git-token-service
Open

Cloudflare Git Token Service - Add new worker for GitHub token management#19
eshurakov wants to merge 1 commit intomainfrom
eshurakov/cloudflare-git-token-service

Conversation

@eshurakov
Copy link
Contributor

Summary

  • Add cloudflare-git-token-service Cloudflare Worker for centralized GitHub App token management
  • Implement installation lookup with org membership verification to ensure users can only access installations they're authorized for
  • Support both standard (read/write) and lite (read-only) GitHub App types with per-app credential configuration
  • Add KV-based token caching with automatic TTL to reduce GitHub API calls
  • Include GitHub Actions workflow for automated deployment

with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
workingDirectory: cloudflare-git-token-service
command: deploy --env prod
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: wrangler deploy --env prod requires an env.prod config, but cloudflare-git-token-service/wrangler.jsonc only defines env.dev.

This workflow will likely fail at deploy time unless you either add env.prod or deploy the default env.

Suggested change
command: deploy --env prod
command: deploy

@@ -0,0 +1,10 @@
# GITHUB_APP_ID: The numeric App ID from GitHub App settings
# GITHUB_APP_PRIVATE_KEY: The PEM-encoded private key (base64 encoded for env var storage)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: .dev.vars.example says the private key is base64-encoded, but the runtime code in cloudflare-git-token-service/src/github-token-service.ts expects a PEM string with \\n sequences (it does replace(/\\n/g, "\n")).

If users follow this comment literally and base64-encode the key, token generation will fail.

Suggested change
# GITHUB_APP_PRIVATE_KEY: The PEM-encoded private key (base64 encoded for env var storage)
# GITHUB_APP_PRIVATE_KEY: The PEM-encoded private key (use \\n for newlines when stored in env vars)

}

// 2. Generate token for the installation (not scoped to specific repo)
const token = await this.githubService.getToken(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Token returned by getTokenForRepo is currently installation-scoped (all repos granted to the app install), not repo-scoped.

Given the RPC method is named getTokenForRepo, consider calling GitHubTokenService.getTokenForRepo() with the repo name extracted from params.githubRepo to reduce token blast radius (and help avoid over-privileged tokens if a caller passes an arbitrary owner/repo).


// Validate orgId is a valid UUID if provided, to prevent database errors
if (params.orgId !== undefined && !isValidUuid(params.orgId)) {
throw new Error(`Invalid organization ID format: ${params.orgId}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: findInstallationId() returns structured failures, but invalid orgId currently throws, which will surface as a 500 from the RPC call.

If invalid orgId is a user input possibility, consider returning a typed failure reason instead of throwing to keep error handling consistent.

export function createDatabaseConnection(connectionString: string): Database {
const pool = new Pool({
connectionString,
max: 100,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: pg.Pool max: 100 is high for a Workers/Hyperdrive context and can easily exhaust DB connections under bursty traffic.

Consider lowering the pool size (and/or making it configurable) to reduce connection pressure.

@kiloconnect
Copy link
Contributor

kiloconnect bot commented Feb 4, 2026

Code Review Summary

Status: 6 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 4
SUGGESTION 2

Fix these issues in Kilo Cloud

Issue Details (click to expand)

WARNING

File Line Issue
cloudflare-git-token-service/package.json 6 dev script runs wrangler dev without --env dev, which can use the wrong bindings vs env.dev + break wrangler.test.jsonc service binding expectations
cloudflare-git-token-service/src/github-token-service.ts 105 Private key handling only unescapes \n; .dev.vars.example says the key is base64-encoded, which would fail at runtime unless decoded or docs updated
cloudflare-git-token-service/src/index.ts 59 getTokenForRepo() currently returns an installation-wide token (not repo-scoped), which can grant broader access than intended
cloudflare-git-token-service/src/installation-lookup-service.ts 80 githubRepo format validation is too permissive (includes('/')), allowing values like owner/ or owner/repo/extra to pass

SUGGESTION

File Line Issue
.github/workflows/deploy-git-token-service.yml 18 CI uses pnpm version: latest; pinning reduces surprise breakages and supply-chain exposure
cloudflare-git-token-service/src/db/database.ts 13 pg.Pool max: 100 may be high for Workers/Hyperdrive and could cause connection pressure
Files Reviewed (12 files)

GITHUB_LITE_APP_ID?: string;
GITHUB_LITE_APP_PRIVATE_KEY?: string;
};

const TokenCacheEntrySchema = z.object({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like TokenCacheEntry, GeneratedToken, and TokenCacheEntrySchema are all the same. Should probably consolidate into a single type inferred from the zod schema:

type Token = z.infer<typeof Token>
const Token = z.object({
  token: z.string(),
  expiresAt: z.number(),
});

${platform_integrations.github_app_type}
FROM ${platform_integrations}
-- For org installations, verify user is a member of the org
LEFT JOIN ${organization_memberships}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we also need to join on kilocode_users and ensure that blocked_reason is null?

@eshurakov eshurakov force-pushed the eshurakov/cloudflare-git-token-service branch from c1ae901 to 0e246fc Compare February 5, 2026 08:59
- Implement installation lookup with org membership verification
- Support both standard (read/write) and lite (read-only) GitHub App types
- Add KV-based token caching with automatic TTL
- Include blocked user check to prevent blocked users from getting tokens
- Add GitHub Actions workflow for automated deployment
@eshurakov eshurakov force-pushed the eshurakov/cloudflare-git-token-service branch from 0e246fc to 443d3b3 Compare February 5, 2026 09:03
}

// Validate githubRepo format (expected: "owner/repo")
if (!params.githubRepo || !params.githubRepo.includes('/')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: githubRepo format validation is too permissive

This currently only checks includes('/'), which means inputs like owner/ (empty repo) or owner/repo/extra will pass and repoOwner will be derived from the first segment anyway. If callers rely on this for input validation, it may lead to surprising lookups.

Suggested change
if (!params.githubRepo || !params.githubRepo.includes('/')) {
if (!params.githubRepo || !/^[^/]+\/[^/]+$/.test(params.githubRepo)) {

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.

2 participants