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.
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.CODEonly — the route produces no chunks and no embeddings, soGRAPH_COMPLETION,CHUNKS, andRAG_COMPLETIONdo 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-onlyand nothing else. Cognee never writes to GitHub.
Prerequisites
- A Cognee
devcheckout — 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).
gitavailable on the Cognee host — repositories are shallow-cloned into~/.cognee/repos.
Setup
1. Create the GitHub App
Go to github.com/settings/apps → New 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.
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.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 aggregateGET /api/v1/integrations/status reports the provider generically, but there is no Connect button yet. Drive the flow through the API:
/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.
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, namedgithub_<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:
code_query operations (explore, traverse, find_path, impact_analysis, delta).
Keeping It in Sync
After the initial sync, webhook deliveries toPOST /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_idis never trusted on its own. It arrives on an unauthenticated endpoint and is a small, guessable integer. The OAuthcodeis exchanged for a user token, that user’s access to the installation is confirmed againstGET /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 reachesgitthrough environment-level config instead. The persisted git remote, the recordedsourceon 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 Cognee —
DELETE /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 thegithub_<org>dataset stays exactly as it is. - Uninstalling the app on GitHub fires the
installationdeletedwebhook, which revokes the credential server-side — the same end state, reached from the other side. This is what actually cuts off GitHub’s access.
forget().
Troubleshooting
POST /authorizereturns 503 “github integration is not configured on this server”:GITHUB_APP_SLUGorGITHUB_WEBHOOK_SECRET(it signs the state) is unset. The backend log names the exact variable. A missingGITHUB_FRONTEND_BASE_URLsurfaces later, as the same 503 from the callback instead.- Redirected to
?github=error_invalid_state: the install didn’t start from a freshly minted/authorizeURL, or more than 10 minutes passed between minting it and approving the install. Mint a new one. - 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 nocodearrives and the exchange has nothing to trade), mismatchedGITHUB_CLIENT_ID/GITHUB_CLIENT_SECRET, aGITHUB_APP_PRIVATE_KEYthat isn’t an RSA PEM, or a missingINTEGRATION_CREDENTIALS_KEY. - Redirected to
?github=error_already_connected: this installation is bound to another Cognee user. Disconnect it there first. - 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>.Nis what the app can actually see, so if it’s0or too low, widen the installation’s repository access on GitHub (that firesinstallation_repositoriesand syncs the additions). - Webhook deliveries show 401 in GitHub’s “Recent Deliveries”:
GITHUB_WEBHOOK_SECRETdoesn’t match the secret set on the app — easy to hit when juggling more than one app. Re-copy it and restart the backend. - 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. - 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.
GRAPH_COMPLETIONorCHUNKSsearch finds nothing from a connected repo: expected — the code route stores graph facts only, with no chunks or embeddings. UseSearchType.CODE.
Related
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.