Code hosts
Grove discovers repositories through the APIs of GitHub, GitLab and Bitbucket Server. Connect as many hosts as you like and mix providers: a GitHub organization, a self-hosted GitLab and a Bitbucket Server can share one Grove instance.
Adding a connection
As a superuser, go to Settings → Code hosts, click Add code host and pick the provider. Edit the JSON configuration (the editor validates it as you type) and save. Grove syncs right away and again every RESYNC_INTERVAL minutes (30 by default); Sync now starts a sync on demand.
Tokens are encrypted with SECRET_KEY before they’re stored. When you edit a host later, a stored token shows as <REDACTED> and is kept unless you replace it.


GitHub
Personal access token
Create a token at GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens (or classic tokens). The minimum scopes required are:
- Contents — read (to clone repositories)
- Metadata — read (to list repositories)
The token must be able to see every repository you want indexed; for a fine-grained token, set its resource owner to the organisation.
{
"url": "https://github.com",
"authToken": "github_pat_…",
"organisations": ["myorg"],
"projectQueries": ["otherorg/some-repo"]
}
Config fields
| Field | Required | Description |
|---|---|---|
url |
Yes | https://github.com, or your GitHub Enterprise Server URL |
apiURL |
No | API base URL, for GitHub Enterprise Server installations with a non-standard API path |
authToken |
Yes | Personal access token |
organisations |
No | Index every repository in these organisations |
projectQueries |
No | Individual repositories to index, as exact owner/repo paths |
excludes |
No | owner/repo patterns to skip (see Excluding repositories) |
GitHub Enterprise Server
Point url at your GHES instance and optionally set apiURL if your API lives at a non-standard path:
{
"url": "https://github.example.com",
"apiURL": "https://github.example.com/api/v3",
"authToken": "ghp_…",
"projectQueries": ["myorg/myrepo"]
}
GitLab
Personal access token
Create a token at GitLab → User settings → Access tokens. Required scopes:
read_api— to list groups and projectsread_repository— to clone repositories
{
"gitlabURL": "https://gitlab.com",
"authToken": "glpat-…",
"groups": ["mygroup"],
"projects": ["mygroup/myproject"]
}
Config fields
| Field | Required | Description |
|---|---|---|
gitlabURL |
Yes | https://gitlab.com or your self-hosted GitLab URL |
authToken |
Yes | Personal access token |
groups |
No | Sync all projects in these groups (including subgroups) |
projects |
No | Sync specific projects by namespace/path |
excludes |
No | namespace/path patterns to skip |
Projects without a default branch (empty projects) are skipped. To skip a group’s subgroups, append ?include_subgroups=false to its name, e.g. "mygroup?include_subgroups=false".
Token rotation
Grove automatically rotates expiring GitLab personal access tokens. When a token has an expiry date, Grove schedules rotation ahead of it (35 days before for tokens expiring in more than 2 months; 7 days before otherwise). The rotated token is persisted back to the database — no manual intervention needed.
Token rotation requires the token to have been created as a personal access token (not a project or group token), and the GitLab instance must support the /api/v4/personal_access_tokens/self/rotate endpoint (GitLab 16.0+).
Self-hosted GitLab
Point gitlabURL at your instance:
{
"gitlabURL": "https://gitlab.example.com",
"authToken": "glpat-…",
"groups": ["platform-team"]
}
Bitbucket
Grove supports Bitbucket Server / Data Center.
Personal access token
Create a token under Manage account → Personal access tokens with Repository read permission.
{
"baseURL": "https://bitbucket.example.com",
"username": "myusername",
"authToken": "…",
"projects": ["PLATFORM"],
"repos": ["TOOLS/deploy-scripts"]
}
Config fields
| Field | Required | Description |
|---|---|---|
baseURL |
Yes | Your Bitbucket Server URL |
username |
Yes | Your Bitbucket username (used for cloning) |
authToken |
Yes | Personal access token |
projects |
No | Index every repository in these projects, by project key |
repos |
No | Individual repositories to index, as PROJECTKEY/slug |
excludes |
No | PROJECTKEY/slug patterns to skip |
If neither projects nor repos is set, Grove indexes every repository the token can access.
Excluding repositories
All three providers support an excludes field. Each entry is matched against the repository’s full path (owner/repo on GitHub, namespace/path on GitLab, PROJECTKEY/slug on Bitbucket). Entries can be exact paths or wildcard patterns, where * matches any characters except /:
{
"excludes": ["myorg/legacy-monolith", "myorg/sandbox-*"]
}
Patterns use glob syntax, so myorg/sandbox-* matches every repository with that prefix.
Removed and archived repositories
Archived repositories are indexed but left out of search results unless a query says archived:yes or archived:only. Repositories that disappear from a code host, or that a new exclude matches, are marked as deleted (see the Deleted filter under Settings → Repositories). Set ENABLE_REPO_DELETION=true to have Grove remove them, including their mirrors and indices.
Fetch schedule
After the first clone, Grove fetches each repository on its own schedule, more often for repositories that change often. Settings → Repositories shows the last and next fetch for each one and lets you fetch or reindex a repository right away.