Git Integration connects Cortex directly to your repositories. A connected repo becomes a bidirectional interface: Cortex ingests its files and wiki into the knowledge graph (read), and the research agent can act on it by opening pull requests (read/write). It works with GitHub, GitLab, and Gitea, including self-hosted instances.
Enable it with ENABLE_GIT_INTEGRATION=true, then manage connections from Settings → Git Integration.
Overview
Each connection has an access level that decides what Cortex can do:
| Access level | What it enables |
|---|---|
| Read-only | Ingestion only — repo files and (optionally) the wiki flow into the knowledge graph, searchable alongside your other documents. |
| Read/write | Ingestion plus the agent's git_repo tool — it can read live files and open pull requests with proposed changes. |
A repo's content is chunked, embedded, and run through entity/relationship extraction exactly like uploaded documents — so once synced, it shows up in Search, Ask AI, the knowledge graph, and communities.
Connecting a repository
- Go to Settings → Git Integration and click Connect repository.
- Pick a provider (GitHub / GitLab / Gitea). For self-hosted GitLab/Gitea, fill in the API base URL. Its scheme (
http://orhttps://) is honored for both the REST API andgit clone, so a plain-HTTP LAN instance works without any TLS allowlist. - Paste a personal access token. The form shows an inline, vendor-specific guide telling you exactly which token to create — always the least-privilege option — with a direct link to the right settings page. Click Test to verify it.
- Enter the owner/org and repository.
- Choose the access level.
- Pick the collection the repository's files (and wiki pages) should be filed into. Leave it on Default collection to use the instance default. The picker only appears when collections are enabled.
- Leave the default "Only ingest
.pdfand.mdfiles" checked, or uncheck it to set custom filters (see Filtering). - Click Connect, then Sync to ingest.
Which token to create
Cortex always recommends the most narrowly-scoped token that works:
- GitHub — a fine-grained personal access token scoped to the one repo, with Contents: Read-only for ingestion. For read/write, add Contents: Read and write + Pull requests: Read and write. (To ingest a GitHub wiki, use a classic token with the
reposcope — fine-grained tokens don't cover wikis.) - GitLab — a Project Access Token with role Reporter and scopes
read_api+read_repository(read_repositorycovers the clone,read_apilets Cortex resolve the project, browse your projects and read the wiki). For merge requests, use role Developer withapi+write_repository. With a fine-grained personal access token (GitLab 18.10+), grant Code: Download, Project: Read and Repository: Read on the repository, plus Wiki: Read for the wiki — User: Read is not needed; the Test button then shows "Token accepted" without a username. - Gitea — a scoped personal access token with Repository: Read. For pull requests, Repository: Read and Write plus Issue: Read and Write (for PR comments).
The token is stored server-side, masked in the UI (••••abcd), injected automatically into every git and API call, and never exposed to the agent or written into logs.
Filtering: what gets ingested
By default, new connections ingest .pdf and .md files only — a sensible, safe default for documentation. Uncheck "Only ingest .pdf and .md files" to reveal include and exclude glob fields (gitignore-style patterns, comma-separated), for example:
- Include:
src/**, docs/** - Exclude:
**/node_modules/**, *.lock
Supported file types are text/code (.py, .ts, .go, .md, …) and documents (.pdf, .docx, .pptx, …). Code and markdown ingest through a fast path that skips Docling; PDFs and Office files route through Docling. Images and audio are not ingested from repos.
Incremental sync
Re-syncing is incremental — Cortex does not re-ingest the whole repo each time. It records the last-synced commit and uses git history to classify what changed:
| Change | What Cortex does |
|---|---|
| Added | Creates a new document |
| Modified | Updates that document in place — only the chunks whose text changed are re-extracted; unchanged parts keep their entities, relationships and your edits |
| Deleted | Flags the document for review (never auto-deleted) |
| Renamed | Remaps the document's path |
Aborting a running sync (the Abort button on the Knowledge Graph page) stops it cleanly; the next sync picks up where it left off and skips files that already finished.
If the branch was force-pushed or you change the filters, sync self-heals via a full-tree reconcile (comparing every file's content hash to what's stored). After any change, the graph is flagged stale so you know to re-run relationship analysis and community detection from the Knowledge Graph page.
Deleted files are flagged, not removed. Open a connection's details to review documents whose source file disappeared from the repo, then delete them from the Documents page if you no longer need them.
Keeping repos up to date
- Manual — click Sync on a connection any time.
- Scheduled — set an Auto-sync interval (minutes) under Advanced; a background poller re-syncs that connection on schedule. No webhooks or public endpoint required.
The agent's git_repo tool (read/write)
When a connection is read/write, the research agent gains a git_repo tool with three actions:
- read_file — fetch a file's current contents (used before editing it).
- propose_change — open a pull request with edits.
- comment — comment on an existing pull request.
This is a tool for changing the repository, not for reading it. Questions about a connected repo's code or docs are answered from the knowledge base, which already holds its ingested files and returns them as citable sources — so a read-only connection deliberately gets no git_repo tool at all. Administrators can override that with RESEARCHER_GIT_TOOL (auto by default, always, or off).
Safety is enforced in code, not just by prompt: every write creates a new cortex/agent-… branch and opens a pull request for human review — the agent never pushes to your default branch. On read-only connections, write actions are refused server-side.
Editing & removing connections
Expand a connection to Edit it — change access level, branch, collection, auto-sync interval, filters, wiki ingestion, or rotate the token. Toggling away from the .pdf/.md default when you have custom filters defined asks for confirmation first.
Changing the collection moves every document already synced from that repository into the new collection on save, so a repo never ends up split across two collections. Switching back to Default collection only affects files synced from then on; existing documents stay where they are.
Delete offers two options: keep the already-ingested documents, or purge them along with the connection.
Configuration
| Variable | Default | Description |
|---|---|---|
ENABLE_GIT_INTEGRATION | false | Master switch for the connector |
GIT_WORK_DIR | ./git_repos | Where clone working copies are cached (mount a volume in production) |
GIT_CLONE_DEPTH | 1 | Shallow-clone depth |
GIT_MAX_REPO_SIZE_MB | 500 | Abort sync above this repo size (0 = unlimited) |
GIT_SYNC_MAX_FILE_SIZE_MB | 5 | Skip files larger than this (0 = no limit) |
GIT_SYNC_POLL_INTERVAL | 5 | Minutes between scheduled-sync checks |
GIT_HTTP_TIMEOUT | 30 | Timeout (seconds) for provider API calls |
GIT_HTTP_INSECURE_HOSTS | (empty) | Comma-separated hosts allowed to skip TLS verification (self-signed self-hosted) |
The backend image bundles git; ensure GIT_WORK_DIR is writable.
See also
- Document Upload — how ingested content flows through the pipeline
- Ask AI — querying ingested repo content
- Agent Skills — the other way to extend the agent with external capabilities