Skip to main content
Connect a GitHub organization (or user account) to Cognee by installing a GitHub App into it. Every repository the installation covers is cloned and indexed into the code graph under a single dataset, and stays fresh through webhooks — a push to a default branch re-indexes that repository, and repositories added to the installation are picked up automatically.
This integration is for self-hosted Cognee deployments — you create and own the GitHub App, so it lives in your own GitHub organization and talks only to your own Cognee backend.
Ships on dev. The connector landed after 1.5.3, so it is not in a tagged release yet — run a dev checkout (git checkout dev && uv sync) until the next release.

What It Does

  • One install covers the whole org. The org admin installs the app once and picks which repositories it can see. Cognee indexes all of them — there is no per-repository connect step.
  • Code graph, not documents. Repositories go through the deterministic code-graph pipeline: typed module/symbol/route/storage/service facts and their edges, with no LLM or embedding calls. Results are reachable through SearchType.CODE only — the route produces no chunks and no embeddings, so GRAPH_COMPLETION, CHUNKS, and RAG_COMPLETION do not cover this content.
  • No stored GitHub tokens. The credential Cognee persists for a GitHub installation carries an empty token payload. Access tokens (~1 hour) are minted on demand from the app’s private key when a sync runs, used, and discarded.
  • Read-only. The app needs Contents: Read-only and nothing else. Cognee never writes to GitHub.

Prerequisites

  • A Cognee dev checkout — see the version note above.
  • A running Cognee backend reachable over HTTPS from the public internet: GitHub delivers webhooks and the OAuth redirect to it and will not call localhost. For local development use a tunnel (ngrok or similar) — see the Slack integration’s ngrok section, which applies verbatim; for production this is your deployment’s normal public domain.
  • Permission to create and install a GitHub App in the target organization (org owner, or a repository admin if the org allows it).
  • git available on the Cognee host — repositories are shallow-cloned into ~/.cognee/repos.

Setup

1. Create the GitHub App

Go to github.com/settings/appsNew GitHub App and configure: Then, from the app’s settings page, collect: the numeric App ID, the app’s slug (the <slug> in github.com/apps/<slug>), a generated private key (.pem download), and the Client ID / Client secret.
Create a separate app per environment (local dev, staging, production). One app has one callback URL and one webhook URL, so pointing a shared app at a laptop routes another environment’s deliveries there.

2. Configure Cognee

Copy the app’s values into your backend’s .env (the same block is in .env.template):
There is no GITHUB_REDIRECT_URI. Unlike Slack, the GitHub App install flow takes its callback URL from the app’s own settings rather than from a query parameter, so the redirect URL is configured in one place only — the app.
Every one of these is checked at use time, not at boot: a deployment with GitHub unconfigured still starts normally, and the first call that needs a missing value fails with GITHUB_<NAME> is not configured rather than a confusing downstream error. POST /authorize turns that into a 503 (github integration is not configured on this server) instead of a 500. Restart the backend after any .env change.

3. Install and Connect

The backend ships without frontend wiring for GitHub in this release — the aggregate GET /api/v1/integrations/status reports the provider generically, but there is no Connect button yet. Drive the flow through the API:
Open that URL in a browser, pick the organization, choose which repositories the app may access, and approve. GitHub redirects back to /api/v1/integrations/github/callback, which binds the installation to your Cognee account and then sends the browser to <GITHUB_FRONTEND_BASE_URL>/integrations?github=connected.
Install from the minted URL, not from the app’s public install page. The signed state is what binds the installation to a Cognee account, and it expires 10 minutes after /authorize returns. An install started without it (or after it expires) lands on ?github=error_invalid_state and stores nothing.
The initial sync of every covered repository is kicked off detached, after the redirect. The browser returns immediately; repositories appear in the graph as the clones and pipeline runs finish, which for a large org can take a while.

What Gets Indexed

Every repository the installation covers lands in one dataset per installation, named github_<account>: the org (or user) login lowercased, with every run of non-alphanumeric characters collapsed to _. Acme-Corp becomes github_acme_corp. One dataset per org rather than per repository means backend access control isolates at the org boundary — per-repo datasets would mean one isolated database per repository. The dataset is owned by the Cognee user who completed the install. Query it with SearchType.CODE:
See the Code Graph guide for the full set of code_query operations (explore, traverse, find_path, impact_analysis, delta).

Keeping It in Sync

After the initial sync, webhook deliveries to POST /api/v1/integrations/github/events keep the graph fresh. Coverage is deliberately narrow in this first cut: Every delivery is authenticated by an HMAC-SHA256 over the raw request body, keyed with GITHUB_WEBHOOK_SECRET and compared against X-Hub-Signature-256. That signature is the entire auth model for the route — it is unauthenticated by design, since GitHub cannot send a bearer token. A bad or missing signature is a 401. Cognee acks the delivery as soon as the signature checks out and handles it detached, so GitHub’s delivery timeout is never in play no matter how long a re-index takes. Deliveries are idempotent and safe to redeliver: unchanged repository snapshots are skipped, and revokes are no-ops on already-revoked rows. A delivery for an installation Cognee doesn’t know (or has revoked) is logged and dropped rather than erroring — GitHub gives no ordering guarantee, so an installation.created arriving before the callback stored the credential is a normal, self-healing state.
The installation token minted for a sync lives about an hour and repositories are cloned sequentially. A very large installation can outlive its token mid-batch; the affected repositories surface as per-repo errors, and the next push (or a manual re-sync) picks them up with a fresh token.

How Access Works

  • Nothing durable is stored but the installation id. Tokens are minted on demand: the app’s private key signs a ~9-minute RS256 JWT, which mints a ~1-hour installation token scoped to that installation’s repositories.
  • The redirect’s installation_id is never trusted on its own. It arrives on an unauthenticated endpoint and is a small, guessable integer. The OAuth code is exchanged for a user token, that user’s access to the installation is confirmed against GET /user/installations, and only then is the authoritative installation record fetched with the app JWT. Without the check, anyone could bind another org’s installation — and read access to its private repositories — to their own account.
  • One installation, one Cognee account. An installation already connected to a different user is refused (?github=error_already_connected) rather than silently reassigned.
  • Tokens never ride a URL. Clone URLs stay credential-free (https://github.com/org/repo.git); the token reaches git through environment-level config instead. The persisted git remote, the recorded source on each result item, log lines, and git error output all use the credential-free form, so a token cannot leak through any of them.

Disconnect vs. Uninstall

They are not the same, and neither deletes indexed data:
  • Disconnect in CogneeDELETE /api/v1/integrations/github/connection — marks the stored credential revoked. No further installation tokens are minted, so syncs stop. The app stays installed on GitHub, and the github_<org> dataset stays exactly as it is.
  • Uninstalling the app on GitHub fires the installation deleted webhook, which revokes the credential server-side — the same end state, reached from the other side. This is what actually cuts off GitHub’s access.
Cognee deliberately does not uninstall the app on your behalf during a disconnect: the GitHub-side equivalent of a token revoke is deleting the installation for the entire org, which is too destructive for a per-account action. To remove the indexed data itself, use forget().

Troubleshooting

  1. POST /authorize returns 503 “github integration is not configured on this server”: GITHUB_APP_SLUG or GITHUB_WEBHOOK_SECRET (it signs the state) is unset. The backend log names the exact variable. A missing GITHUB_FRONTEND_BASE_URL surfaces later, as the same 503 from the callback instead.
  2. Redirected to ?github=error_invalid_state: the install didn’t start from a freshly minted /authorize URL, or more than 10 minutes passed between minting it and approving the install. Mint a new one.
  3. Redirected to ?github=error_exchange_failed: the callback never 500s — it catches everything and redirects, and the traceback is in the backend log. Usual causes: “Request user authorization (OAuth) during installation” is off on the app (so no code arrives and the exchange has nothing to trade), mismatched GITHUB_CLIENT_ID/GITHUB_CLIENT_SECRET, a GITHUB_APP_PRIVATE_KEY that isn’t an RSA PEM, or a missing INTEGRATION_CREDENTIALS_KEY.
  4. Redirected to ?github=error_already_connected: this installation is bound to another Cognee user. Disconnect it there first.
  5. Connect succeeds but no repositories appear: the initial sync runs detached and can take a long time on a large org — check the backend log for Syncing N GitHub repositories for <org> into dataset github_<org>. N is what the app can actually see, so if it’s 0 or too low, widen the installation’s repository access on GitHub (that fires installation_repositories and syncs the additions).
  6. Webhook deliveries show 401 in GitHub’s “Recent Deliveries”: GITHUB_WEBHOOK_SECRET doesn’t match the secret set on the app — easy to hit when juggling more than one app. Re-copy it and restart the backend.
  7. Webhook deliveries show 404: the provider name in the URL is wrong, or the backend is running a build without the GitHub connector registered. The route is /api/v1/integrations/github/events.
  8. A push doesn’t re-index anything: only pushes to a repository’s default branch trigger a re-index. Pushes to other branches are ignored by design.
  9. GRAPH_COMPLETION or CHUNKS search finds nothing from a connected repo: expected — the code route stores graph facts only, with no chunks or embeddings. Use SearchType.CODE.

Code Graph

The pipeline this connector feeds, and every SearchType.CODE operation.

Slack

The other first-party OAuth connector, sharing the same credential store and connect flow.