Index documentation straight from a git repository. Contextator keeps a shallow, single-branch checkout on the server and fetches the branch tip at the start of every index run — or the moment someone pushes, if you add a webhook.
Works with any HTTPS git server: GitHub, GitLab, Bitbucket, Gitea, Forgejo, Codeberg and self-hosted instances.
Adding one
Add source → Git repository
| Field | Example | Notes |
|---|---|---|
| Name | api-docs |
The mount prefix. Immutable |
| Repository URL | https://github.com/org/repo.git |
HTTPS only — SSH remotes are not supported |
| Branch | main |
The single branch that is checked out |
| Subdirectory | docs |
Optional. Index only this folder of the repository |
| Access token | – | Only for private repositories. Stored encrypted, never shown again |
| Username | – | Only needed for a GitLab deploy token (gitlab+deploy-token-N) or a Bitbucket Cloud API token (your Bitbucket username or x-bitbucket-api-token-auth) |
| File types | .md, .mdx |
.txt, .html/.htm, .csv, .docx and .pdf can be selected too |
Press Test connection before saving: it lists the remote’s refs without cloning and answers with the branch’s current commit, so a wrong URL, branch or token is obvious immediately.
With a subdirectory set, a file at docs/guides/install.md in the repository is indexed as
api-docs/guides/install.md — the subdirectory itself does not appear in the path.
Private repositories
Paste an access token into Access token. It is encrypted with SECRET_KEY (AES-256-GCM) before it
is stored, is never returned by the API and is never shown again — the dialog only says that a token
exists. To replace it, paste a new one; to remove it, tick Remove the stored token.
SECRET_KEYmust be set (32+ characters, e.g.openssl rand -hex 32) before a source can store a token. See Configuration.
The username sent with the token depends on the provider and is detected from the URL:
| Provider | Username used | Token types |
|---|---|---|
| GitHub | x-access-token |
Classic PAT, fine-grained PAT, App installation token |
| GitLab | oauth2 |
OAuth tokens, personal and project access tokens |
| Bitbucket Cloud | x-token-auth |
Repository and workspace access tokens |
| Bitbucket Cloud (API token) | your Bitbucket username, or x-bitbucket-api-token-auth |
Put it in the Username field — the default x-token-auth is for repository/workspace access tokens only |
| Gitea / Forgejo / Codeberg / other | token, or whatever you type in Username |
Credentials pasted into the URL itself (https://user:token@host/…) are stripped before the URL is
stored — use the token field instead.
Recommended token scopes
Read access to the repository’s contents is enough — the narrowest credential each provider offers:
| Provider | Narrowest token | Username |
|---|---|---|
| GitHub | A fine-grained PAT limited to the repository with Contents: read, or a GitHub App installation token | leave empty (x-access-token) |
| GitLab | A project deploy token with read_repository |
the token’s generated username, e.g. gitlab+deploy-token-42 — the default oauth2 is refused for a deploy token |
| Bitbucket Cloud | A repository access token with Repositories: read | leave empty (x-token-auth) |
| Gitea / Forgejo | An access token with read scope on the repository | your username, or leave empty (token) |
Reaching a repository only available over SSH
Git is read over HTTPS only. SSH remotes (ssh://…, git@host:path) are not accepted and no SSH
key can be stored (ADR-0086). For a repository reachable only over SSH, mirror it yourself: clone or
mirror it on the host, inside ALLOWED_DOC_ROOTS, keep it current on your own schedule (a cron job
running git pull, or your CI), and add that directory as a
Local Directory Source instead. It is then a folder like any other —
no push webhook, no branch or subdirectory setting, no Test connection — and exactly as fresh as your
schedule keeps it.
How syncing works
At the start of every index run:
- If there is no checkout yet, clone it: shallow (
depth=1), single branch, no tags. - Otherwise fetch the branch tip. If it moved, point the local branch at it and check out.
- If anything goes wrong, fall back to a fresh clone.
- Verify the subdirectory still exists on that branch.
The commit currently checked out is shown on the source row (main @ a1b2c3d). Checkouts live under
DATA_DIR, so they survive restarts and are deleted with the source.
Automatic re-indexing on push
Every git source gets its own webhook URL and secret, shown while editing the source:
POST http://<your-host>/api/webhooks/git/<source-id>
Add it as a push webhook in the repository settings with that secret, and every push to the branch queues a re-index. Full instructions and provider-by-provider screenshots of the fields: Push Webhooks.
Common problems
| Symptom | Cause and fix |
|---|---|
| Authentication failed on Test connection | Token missing, expired, or lacking read access. Leave Username empty for a Bitbucket repository or workspace access token (the default x-token-auth is for those only); a Bitbucket Cloud API token needs your Bitbucket username or x-bitbucket-api-token-auth, and a GitLab deploy token its generated gitlab+deploy-token-N username |
| Branch “…” was not found on the remote | The branch name is wrong, or the default branch is master rather than main |
| Subdirectory “…” does not exist in the repository | The path is relative to the repository root and is checked against the branch you configured |
| Documents disappeared after a failed sync | They do not — a source that cannot be read keeps its documents. Fix the cause and press Sync |
| SSH URL rejected | Only HTTPS is supported. Use the HTTPS URL with a token |
Notes and limits
- Only one branch per source. To index two branches, add two sources with different names.
- Very large repositories clone more slowly than the native
gitbinary would;depth=1, a single branch and a subdirectory keep it comfortable for documentation. - Submodules are not fetched.
- The repository is never written to; Contextator only ever fetches.