GitLab
What it gives you — the Learn loop on a self-hosted or gitlab.com project: verified root causes drafted as merge requests against your knowledge-catalog repo, via a scoped project or group access token. This is what lets a self-hosted GitLab team — the archetype of RunLore’s lock-in-averse, sovereignty-conscious audience — use the curation loop at all.
Minimal config
forge:
provider: gitlab # github (default) | gitlab
kb_repo: your-group/runlore-kb
base_branch: main
gitlab:
base_url: https://gitlab.example.com # omit for gitlab.com
token_env: GITLAB_TOKENCreate the token
GitLab has no GitHub-App equivalent bot identity — OAuth apps are the wrong shape for a headless
curator, so auth is a project or group access token, sent as the PRIVATE-TOKEN header on every
request.
- Project → Settings → Access Tokens (or Group → Settings → Access Tokens to scope one token across every project group-wide).
- Role: Developer at minimum — creating a branch, committing the drafted entry, and opening a
merge request all need it. Give Maintainer only if
base_branchis a protected branch your project requires a higher role to push feature branches against. - Scope:
api— the full API scope; GitLab has no finer-grained scope that still covers creating commits, merge requests, and labels. - Set an expiration date and rotate before it lapses — an expired token fails closed (every forge call 401s), it does not silently disable curation.
- Copy the token once (GitLab shows it only at creation time) and store it as a Secret:
kubectl -n runlore create secret generic runlore-secrets \
--from-literal=GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxxVerify it locally
kubectl -n runlore logs deploy/runlore | grep 'curator enabled'Fire a test incident that produces a verified root cause and confirm a merge request lands on the KB project:
kubectl -n runlore logs deploy/runlore | grep 'msg=curated url='Check the token’s effective scope directly against the API (a 404 here — rather than a permissions error — is almost always the URL-encoding gotcha below, not a scope problem):
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.example.com/api/v4/projects/your-group%2Frunlore-kb"Notes
kb_repois a project PATH, not a numeric ID (group/project, or a nestedgroup/subgroup/project) — RunLore URL-encodes it into the API path (group%2Fproject) on every call, per the GitLab v4 API’s own requirement. Getting this encoding wrong is the single most common GitLab-client bug, and it 404s in a way that reads exactly like a permissions problem, so check the encoding first if merge requests never appear.base_urlis the instance root, e.g.https://gitlab.example.com— RunLore appends/api/v4itself. Omit it entirely for gitlab.com.- Auth is a static credential (unlike GitHub’s short-lived, minted App installation tokens) —
referenced by env-var indirection only (
token_envnames the variable; the config file never holds the token itself). Never commit the token or put it invalues.yaml. Store it in aSecret, ideally synced from a vault via External Secrets, and rotate it periodically. provider: gitlabwith noforge.gitlab.token_env, or akb_repothat isn’t a valid GitLab project path, both fail config load closed —serverefuses to start rather than coming up with curation silently disabled.- TLS verification is always on; there is no
insecure_skip_verifyescape hatch. A self-signed instance needs a certificate your cluster’s trust store already accepts. - Tested against GitLab 16.x and 17.x (self-managed) and gitlab.com; the API surface RunLore uses (Commits, Merge Requests, Issues, Notes) has been stable across the v4 API for years, so earlier 16.x/15.x installs are likely fine too, just not part of the tested matrix.
- RunLore’s writes are confined to the forge — it has no cluster-mutating permissions.
Not yet supported on GitLab
The Learn loop is complete on GitLab: an investigation drafts a merge request, a recurrence
coalesces onto the open one instead of duplicating it, the reinvestigate label re-runs the
investigation and posts the findings back, and index.md/log.md stay in step with every entry.
Phase-2 grooming is GitHub-only. These do not work on a GitLab-hosted knowledge base today:
| What | Behaviour on GitLab |
|---|---|
lore curate (the backlog-grooming CLI: dedup, lifecycle, queue, recurrence, contested, retirement passes) | Fails to start, with a message naming a GitHub App — it has no GitLab code path |
In-server sweeps (curate.sweeps.mode: apply) | Silently disabled — no sweeper is built, and nothing in the logs says so |
Source-repo diff cloning for private repos (gitops source cloning) | GitHub-App auth only — unrelated to which forge hosts the KB, but it bites the same operator |
Concretely: nothing prunes the KB backlog, stale merge requests are never closed, and entries are
never promoted to ready-to-merge when the underlying incident resolves. Review and merge KB merge
requests by hand until GitLab support lands in the grooming agent.
Reference
- Configuration →
forgefor the full key reference. - Learning loop — the full curate/triage lifecycle. Note that it documents the GitHub lifecycle end to end; see Not yet supported on GitLab for the Phase-2 grooming passes a GitLab-hosted KB does not get yet.
- GitHub — the other supported forge, for a side-by-side comparison of the two auth models.