Skip to main content

GitHub Source connector (Nango-brokered) โ€” design

Date: 2026-08-03 Status: approved (design), pending implementation Branch: feat/github-source-connector (off origin/dev)

Goalโ€‹

Make GitHub a client-connectable Source: an org admin authorizes GitHub via OAuth on the Sources page (/client/settings/sources), the token lives in Nango, and the already-shipped runtime tools consume the org's connection instead of relying on script-imported connections or the anonymous public-repo fallback.

What already exists (not in scope)โ€‹

The entire runtime consumption path shipped earlier and needs no changes:

  • github_read (kind: 'nango', handler github) in api/src/agent-api/tool-registry.ts โ€” listIssues / listPullRequests / listCommits. The executor (runtime-tool-executor.service.ts) proxies through NangoService.proxy({ provider: 'github', connectionId: <stored> }) when an org credential exists, and falls back to anonymous public-repo reads (executePublicGitHubRead) when it doesn't.
  • github_write (kind: 'bespoke', handler github_first_party) โ€” GitHubFirstPartyToolServerService resolves the same integrationType: 'github' credential row and proxies writes through the same Nango connection. One connection feeds both tools.
  • owner/repo are per-call tool params โ€” no per-connection configuration is needed, so the Sources tile is plain connect/disconnect (no drawer, unlike Slack Archive).
  • testNangoConnection is provider-generic (GET /connection/:id) โ€” no new branch.

What's missing โ€” the connector plumbingโ€‹

'github' is absent from every provider registry, so it cannot be connected through the Nango session/confirm flow and never appears on the Sources page. This design adds it, mirroring the google_workspace addition (docs/superpowers/plans/2026-07-28-google-workspace-read-tools.md) โ€” same touchpoints, no new architecture.

Decisionsโ€‹

DecisionChoiceWhy
OAuth modelGitHub OAuth App, repo scope (Nango provider github)Matches the plain Connect-session flow every other provider uses and the existing seeds/fixtures (nango_scopes: ['repo']). A GitHub App (fine-grained, installation-based) would deviate from the shared session/confirm flow โ€” rejected by user 2026-08-03.
Scope deliveryConfigured on the Nango integration (dashboard), not NANGO_USER_SCOPESThe user/bot scope split is Slack-specific; GitHub scopes ride the provider-level scope= param.
Tile gatingSOURCE_TOOLS_BY_PROVIDER.github: ['github_read', 'github_write'] โ€” any-enabled shows the tileBoth tools consume this one connection; same any-enabled rule as google_workspace's three tools.
Display nameGitHubโ€”
DB migrationNoneintegration_type is varchar; precedent: slack_archive, google_workspace.

Changesโ€‹

Backend (api/src/integrations/credentials/)โ€‹

  1. credentials.dto.ts
    • 'github' added to the ChannelType union (after 'google_workspace').
    • 'github' added to NANGO_CONNECT_PROVIDERS (single source of truth for both DTO @IsIn allow-lists and the Sources/connect-token surfaces).
  2. credentials.service.ts
    • CHANNEL_REQUIRED_KEYS.github: [] (creds live in Nango; only nango_connection_id is stored locally).
    • 'github' added to NANGO_BACKED_TYPES (gives create/confirm, delete with Nango-side revoke, and the generic connection test for free).
    • validateNangoProvider: widen the error string and the Extract<...> return type.
    • No NANGO_USER_SCOPES entry (see Decisions).
  3. sources.service.ts
    • SOURCE_TOOLS_BY_PROVIDER.github: ['github_read', 'github_write'].
    • SOURCE_DISPLAY_NAME.github: 'GitHub'.

Everything downstream keys off these sets and picks GitHub up automatically: the Connect session/confirm endpoints (NangoCredentialsController), delete with provider-side revoke, testIntegration, the Sources read model, the connect_integration agent tool (integrationTypes: [...NANGO_CONNECT_PROVIDERS]), the public connect-token flow, and ToolPermissionsGuard's provider gating.

Frontend (frontend/src/)โ€‹

  1. lib/api.ts โ€” 'google_workspace' and 'github' are present in the SourceInfo.provider union and NANGO_SOURCE_PROVIDERS classifier (comment there says: mirrors NANGO_CONNECT_PROVIDERS, update together). This closes the pre-existing Google Workspace mirror drift while adding GitHub, so one-argument source-provider calls use the authenticated /v1 source routes.
  2. app/client/settings/sources/page.tsx โ€” GitHub entry in the provider icon/meta map using siGithub from simple-icons (the idiom the other tiles use). No other page changes: tiles render from the API response.

Testsโ€‹

  • credentials.nango-provider-lockstep.spec and sources.service.spec (map-completeness guard) are loop-driven over the provider sets โ€” they extend to github automatically and fail if any set is missed.
  • tool-permissions.guard.spec pins the Connect-session/confirm permission gate for GitHub's two-tool any-enabled mapping, rejecting when neither github_read nor github_write is enabled and allowing when either one is.
  • credentials.delete.spec pins the GitHub disconnect lifecycle: the service revokes the stored Nango-generated connection id as provider github before removing the local row, and fail-closes without local removal if revoke fails.
  • Add a github case to the Nango session-mint spec asserting the session carries no integrations_config_defaults / user_scopes (the inverse of the slack_search/slack_archive assertions), pinning the scopes-live-on-the-integration decision.
  • Frontend: api.test.ts pins the Nango helper overload routing for source-provider calls (google_workspace/github) and the existing org-scoped GitHub card call shape; page.github.test.tsx renders the Sources-page GitHub tile and pins the org-scoped Connect flow.

Error handlingโ€‹

No new error paths. Connect failures surface through the existing session/confirm flow (generic {success:false} on the public path, HTTP errors on the authenticated path); a missing connection keeps github_read on the public fallback and github_write on its existing 404 ("GitHub is not configured for this organization").

Out-of-repo handoff (user)โ€‹

  1. Create a GitHub OAuth App (org-owned), callback URL = Nango's callback.
  2. In the Nango dashboard: create integration with provider github, unique key github, the OAuth App client id/secret, scope repo.
  3. SuperAdmin enables github_read (and optionally github_write) tool permissions for the pilot org โ€” deny-by-default hides the tile until then.

Verificationโ€‹

  • Targeted jest: the two lockstep/completeness specs + the new session-mint case + existing credentials/sources specs touched by the sets.
  • frontend: build + rendered Sources page against the Railway-hosted Nango (rendered-UI rule) once the dashboard integration exists.
  • Smoke: connect a real org โ†’ credential row stores the Nango-generated connection id โ†’ github_read on a private repo succeeds โ†’ disconnect revokes at Nango and removes the row.