GitLab
Proxy for the GitLab REST API via the managed gateway.
What this connector does
Gives agents gated access to a GitLab instance (SaaS or self-hosted) for:
- Listing users, projects, merge requests.
- Creating and editing merge requests.
- Commenting on merge requests.
- Minting project access tokens.
- Listing container-registry repositories and tags.
All operations are gated by PBAC policy. Every call flows: agent → gateway → /introspect (with this connector's resource_type) → OPA decision → GitLab API → response.
The connector supports two auth modes, chosen per managed resource at install time:
- Per-user passthrough (
idp_passthrough, recommended) — the gateway forwards each calling user's own GitLab OAuth token (captured when they log in to PolicyArc through the GitLab IdP). GitLab enforces its own project membership and permissions, and its audit trail shows the real person. - Shared service token (
static) — every call uses one operator-held personal/project/group access token via thePRIVATE-TOKENheader. PolicyArc still enforces per-user policy and audit in front, but GitLab sees a single service identity.
Prerequisites
- The base URL of your GitLab instance (defaults to
https://gitlab.com). - Passthrough mode only: a GitLab IdP registered in PolicyArc with
provider key
gitlab(use the GitLab preset in the IdP setup wizard). The OAuth application on your GitLab instance must request theapiscope in addition to the OIDC login scopes (openid profile email). Users must log in to PolicyArc through that GitLab IdP — the gateway replays the token captured at login. - Static mode only: a GitLab access token that has enough permission for the operations you want to gate. Options:
- Personal Access Token — scoped to one user; simplest for PoC.
- Project Access Token — scoped to one project; recommended for production if the agent only needs one project.
- Group Access Token — scoped to a group of projects.
Install
Pick the auth mode in the install form's Authentication field
(defaults to idp_passthrough). The access-token field only appears when
static is selected; passthrough installs need no secret.
1. Provision the credential (static mode)
For static mode, create a GitLab access token with the minimum scopes you need. For MR automation:
| Operation | Minimum GitLab scope |
|---|---|
| List projects/MRs/notes | read_api |
| Create/edit MRs, post comments | api |
| Create project access tokens | api + project Owner/Maintainer |
| Container registry | read_registry |
Put the token in an environment variable the PBAC AS can read (static mode only):
export GITLAB_PRIVATE_TOKEN="glpat-..."
2. Install the connector
From the admin UI (once the v1 installer ships):
- Resources → Add → GitLab
- Fill in the wizard:
- GitLab base URL —
https://gitlab.comor your self-hosted URL. - Access token (env var name) — the name of the env var holding the token (e.g.
GITLAB_PRIVATE_TOKEN), not the token itself. The AS reads the env var at request time. (Static mode only — this field is hidden whenidp_passthroughis selected.)
- GitLab base URL —
- Install.
The admin UI reads setup_fields directly from this connector's manifest — same fields shown below in the Setup fields reference.
3. Verify
Request a token with a GitLab scope:
curl -X POST https://<your-as>/token \
-u "<agent-client-id>:<agent-client-secret>" \
-d "grant_type=client_credentials&scope=gitlab:read"
Call through the gateway:
curl https://<your-as>/gateway/identos.gitlab/api/v4/projects?search=acme \
-H "Authorization: Bearer <token>"
A successful 200 with the project list confirms: token issued → introspect allowed → gateway proxied → upstream responded.
What can go wrong
| Symptom | Cause | Fix |
|---|---|---|
401 from GitLab (static mode) | GITLAB_PRIVATE_TOKEN env var not set or wrong | Check the AS process environment; confirm echo $GITLAB_PRIVATE_TOKEN resolves in the container. |
403 from the gateway | Agent lacks the required scope | Request a token with gitlab:read, gitlab:write, or gitlab:admin matching the route. |
404 at the gateway | Wrong path | Gateway path is /gateway/identos.gitlab/<GitLab REST path> — GitLab paths start with /api/v4/.... |
| Agent sees an empty project list (static mode) | The PAT's user is in no projects | Use a wider-scope token or add the user to the target projects. |
502 "no stored IdP token" (passthrough mode) | The caller never logged in through the gitlab IdP (or used client_credentials, which has no user context) | Re-authenticate via GitLab. |
GitLab 401/403 upstream (passthrough mode) | The captured token expired and refresh failed, or the OAuth app wasn't granted api | Check the IdP registration's scopes. |
Scopes
| Scope | Meaning | Routes |
|---|---|---|
gitlab:read | Read-only operations | list/get projects, MRs, notes, registries |
gitlab:write | Mutating operations | create/edit MRs, post MR comments |
gitlab:admin | Privileged operations | create project access tokens |
Policy authors can write rules targeting input.resource.type == "urn:connector:identos:gitlab". Per-project rules use the resource_id (gitlab://projects/{project_id}).
Setup fields
| Field | Required | Default | Purpose |
|---|---|---|---|
base_url | yes | https://gitlab.com | GitLab base URL (SaaS or self-hosted). |
upstream_auth.type | yes | idp_passthrough | Authentication — idp_passthrough (per-user, default) or static (shared token). |
token_env | conditional | — | Name of the env var holding the access token (not the token itself). Static mode only; hidden when idp_passthrough is selected. |
MCP tools
When the managed gateway's MCP aggregator is enabled, this connector exposes the following tools to agents:
| Tool | Scope | Description |
|---|---|---|
list_users | gitlab:read | Search GitLab users. |
list_projects | gitlab:read | List projects (optionally filter by search, membership). |
get_project | gitlab:read | Get a project by ID or URL-encoded path. |
list_merge_requests | gitlab:read | List MRs on a project. |
create_merge_request | gitlab:write | Open a new MR. |
list_merge_request_notes | gitlab:read | List comments on an MR. |
create_merge_request_note | gitlab:write | Add a comment to an MR. |
edit_merge_request | gitlab:write | Update MR title/description/state. |
create_project_access_token | gitlab:admin | Mint a project access token. |
list_registry_repositories | gitlab:read | List container registry repositories on a project. |
list_registry_tags | gitlab:read | List tags in a registry repository. |
get_registry_tag | gitlab:read | Get a registry tag's details, including created_at (publish date), digest, and size. |
list_releases | gitlab:read | List releases for a project (newest first). |
get_release | gitlab:read | Get a single release by tag name. |
Each tool's input schema is embedded in the manifest and forwarded verbatim to MCP clients.
Example policy
Block MR creation on a sensitive project unless the caller belongs to the release-managers group:
package pbac.operator.connectors["identos.gitlab"]
deny contains msg if {
input.resource.type == "urn:connector:identos:gitlab"
input.action.method == "POST"
endswith(input.action.path, "/merge_requests")
input.resource.id == "gitlab://projects/release-critical"
not "release-managers" in input.subject.groups
msg := "only release-managers can open MRs against release-critical"
}
This rule lives in the operator namespace (not this connector's) because "which group is allowed" is tenant-specific.
Reference
- Manifest:
connectors/identos.gitlab/connector.v1.jsonin the repo. - Upstream API docs: docs.gitlab.com/ee/api
- Access token types: docs.gitlab.com/ee/user/profile/personal_access_tokens
Manifest reference
- ID:
identos.gitlab - Version:
1.2.0 - Resource type:
urn:connector:identos:gitlab - Capabilities:
mcp
Supported auth modes
| Type | Details |
|---|---|
idp_passthrough | requires IdP gitlab |
static | scheme header; header PRIVATE-TOKEN; setup fields: token_env |
Setup fields
| ID | Label | Default | Secret? | Notes |
|---|---|---|---|---|
base_url | GitLab base URL | https://gitlab.com | no | placeholder: https://git.example.com |
upstream_auth.type | Authentication | idp_passthrough | no | idp_passthrough forwards each user's own GitLab OAuth token (recommended). static uses a shared personal/project/group access token via the PRIVATE-TOKEN header. |
token_env | Access token | — | yes | Pick a secret holding a GitLab personal, project, or group access token. Required only for the static auth mode. / shown when upstream_auth.type == 'static' |
Scopes
| Scope |
|---|
gitlab:read |
gitlab:write |
gitlab:admin |
Routes
| Method | Pattern | Scope | Resource template |
|---|---|---|---|
GET | /api/v4/users | gitlab:read | — |
GET | /api/v4/projects | gitlab:read | — |
GET | /api/v4/projects/{project_id} | gitlab:read | gitlab://projects/{{project_id}} |
GET | /api/v4/projects/{project_id}/merge_requests | gitlab:read | gitlab://projects/{{project_id}} |
POST | /api/v4/projects/{project_id}/merge_requests | gitlab:write | gitlab://projects/{{project_id}} |
GET | /api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/notes | gitlab:read | gitlab://projects/{{project_id}}/mr/{{merge_request_iid}} |
POST | /api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/notes | gitlab:write | gitlab://projects/{{project_id}}/mr/{{merge_request_iid}} |
GET | /api/v4/projects/{project_id}/merge_requests/{merge_request_iid} | gitlab:read | gitlab://projects/{{project_id}}/mr/{{merge_request_iid}} |
POST | /api/v4/projects/{project_id}/merge_requests/{merge_request_iid}/discussions | gitlab:write | gitlab://projects/{{project_id}}/mr/{{merge_request_iid}} |
PUT | /api/v4/projects/{project_id}/merge_requests/{merge_request_iid} | gitlab:write | gitlab://projects/{{project_id}}/mr/{{merge_request_iid}} |
POST | /api/v4/projects/{project_id}/access_tokens | gitlab:admin | gitlab://projects/{{project_id}} |
GET | /api/v4/projects/{project_id}/registry/repositories | gitlab:read | gitlab://projects/{{project_id}} |
GET | /api/v4/projects/{project_id}/registry/repositories/{repository_id}/tags | gitlab:read | gitlab://projects/{{project_id}}/registry/{{repository_id}} |
GET | /api/v4/projects/{project_id}/registry/repositories/{repository_id}/tags/{tag_name} | gitlab:read | gitlab://projects/{{project_id}}/registry/{{repository_id}} |
GET | /api/v4/projects/{project_id}/releases | gitlab:read | gitlab://projects/{{project_id}} |
GET | /api/v4/projects/{project_id}/releases/{tag_name} | gitlab:read | gitlab://projects/{{project_id}}/releases/{{tag_name}} |
MCP tools
| Name | Scope | Description |
|---|---|---|
list_users | gitlab:read | List GitLab users (optional search). |
list_projects | gitlab:read | List GitLab projects. |
get_project | gitlab:read | Get a GitLab project by ID or URL-encoded path. |
list_merge_requests | gitlab:read | List merge requests for a project. |
create_merge_request | gitlab:write | Create a merge request. |
list_merge_request_notes | gitlab:read | List comments on a merge request. |
create_merge_request_note | gitlab:write | Add a comment on a merge request. |
get_merge_request | gitlab:read | Get a single merge request, including diff_refs (base_sha, head_sha, start_sha) — the SHAs needed to anchor an inline discussion comment to specific diff lines. |
create_merge_request_discussion | gitlab:write | Start a discussion thread on a merge request. Supply position to anchor the comment to specific lines of the diff (an inline code-review comment); omit it for a plain MR-level thread. Position SHAs come from the merge request's diff_refs (fetch via get_merge_request). |
edit_merge_request | gitlab:write | Update merge request title, description, or state. |
create_project_access_token | gitlab:admin | Create a project access token. |
list_registry_repositories | gitlab:read | List container registry repositories for a project. |
list_registry_tags | gitlab:read | List tags for a container registry repository. |
get_registry_tag | gitlab:read | Get a container registry tag's details, including created_at (publish date), digest, and total size. |
list_releases | gitlab:read | List releases for a project (newest first). |
get_release | gitlab:read | Get a single release by tag name. |