Skip to main content

SCM Connection Reference

This page is the single source of truth for connecting a source-control provider to a self-hosted Plexicus deployment. For each provider it gives the four things you need and cannot guess:

  1. which authentication flow it uses (OAuth App vs GitHub App vs personal access token),
  2. the exact callback URL(s) to register with the provider,
  3. the exact scopes or permissions to grant,
  4. which chart values, Secret keys, or in-app fields carry the credentials.

Throughout, <your-domain> is the value of global.domain in your Helm overlay (PLEXICUS_DOMAIN on the evaluator) — for example plexicus.acme.com, or plexicus.local on a default evaluator install.

No SCM is required to install

Plexicus installs and runs its first scan with no source-control provider connected at all — the Sandbox onboarding flow scans a pre-configured repository through a synthetic connector. Everything on this page is needed only when you want to scan your own repositories. See Evaluator Installation and Self-Hosted Installation.


Supported providers

ProviderDeploymentFlowCredentials live in
GitHub (github.com)SaaSGitHub App (recommended) or OAuth App — browser redirectChart values + Secret
GitHub Enterprise ServerSelf-managedPersonal access token, or OAuth App, or GitHub AppIn-app connector form, per connection
GitLab (gitlab.com)SaaSOAuth application — browser redirectChart values + Secret
GitLab self-managedSelf-managedOAuth application registered on your instance — browser redirectIn-app connector form, per connection
Bitbucket CloudSaaS onlyOAuth consumer — browser redirectChart values + Secret
GiteaSelf-managedPersonal access token onlyIn-app connector form, per connection
ForgejoSelf-managedPersonal access token onlyIn-app connector form, per connection
Azure DevOpsCloud and Server/TFSPersonal access tokenIn-app connector form, per connection
TFVCOn-premisesShares the Azure DevOps connection and PATIn-app connector form, per connection
Bitbucket Server / Data Center is not supported

Plexicus supports Bitbucket Cloud only. There is no Bitbucket Server / Data Center provider in the platform: no OAuth handler, no repository plugin, no client for Bitbucket Server's /rest/api/1.0 API, and no entry in the connector catalogue. The Bitbucket connector has no instance-URL field and every call it makes is hard-coded to api.bitbucket.org, so it cannot be pointed at a self-managed Bitbucket. If you run Bitbucket Data Center, mirror the repositories to a supported forge or contact engineering@plexicus.ai.

Providers whose credentials live in the in-app connector form need nothing in your Helm overlay: each connection carries its own token or client credentials, entered under Connectors → SCM after the platform is running. Only GitHub, GitLab and Bitbucket Cloud have platform-level (chart-level) credentials, and only for their SaaS instances.


Callback URLs

Every browser OAuth redirect lands on the frontend origin — the same hostname users log in on, with no api. prefix. The chart derives these URLs from global.domain automatically; you only have to register the matching value with the provider.

Registered withCallback URL to registerUsed by
GitHub App / OAuth Apphttps://<your-domain>/api/callback/githubBoth "Sign in with GitHub" and the repository connector
GitHub App (second URL)https://api.<your-domain>/vulnerability-tool/callback/githubThe standalone vulnerability-tool OAuth flow and the App-installation hand-off
GitLab applicationhttps://<your-domain>/api/callback/gitlabRepository connector (gitlab.com and self-managed)
Bitbucket Cloud consumerhttps://<your-domain>/api/callback/bitbucket_cloudRepository connector
Google OAuth client (SSO)https://<your-domain>/api/callback/google"Sign in with Google"
Gitea, Forgejo, Azure DevOps, TFVC(none)Token-based — no redirect, nothing to register
GitHub needs two callback URLs, on two different hosts

This is the single most common self-hosted misconfiguration. Plexicus has two distinct GitHub OAuth surfaces:

  • Sign-in and the repository connector both redirect to https://<your-domain>/api/callback/github — the frontend origin, no api. prefix.
  • The vulnerability-tool flow and the App-installation hand-off use https://api.<your-domain>/vulnerability-tool/callback/github — served by the API, so it does carry the api. prefix. Note the hyphen in vulnerability-tool.

A GitHub App accepts multiple callback URLs (Add Callback URL on the app settings page). Register both. Registering only the first leaves the App installation hand-off dead-ending; registering only the second breaks sign-in and the connector with redirect_uri_mismatch.

GitLab and Bitbucket Cloud have only one callback each — the frontend one.

The browser is what follows these redirects, so a hostname that only resolves on your own machine (a .local name from /etc/hosts, for instance) works fine for OAuth. Inbound webhooks are a different matter — see Webhooks.


GitHub

github.com — register a GitHub App

A GitHub App is preferred over a plain OAuth App: it supports webhooks and per-repository install/uninstall, which an OAuth App cannot do.

On github.com go to Settings → Developer settings → GitHub Apps → New GitHub App (register it under the organisation's settings for an organisation-wide install):

  • Homepage URL: https://<your-domain>

  • Callback URLs — add both, using Add Callback URL for the second:

    • https://<your-domain>/api/callback/github
    • https://api.<your-domain>/vulnerability-tool/callback/github
  • Request user authorization (OAuth) during installation: checked.

  • Webhook → Active: check it only if your <your-domain> is resolvable from GitHub's network — see Webhooks.

  • Repository permissions:

    PermissionLevelNeeded for
    MetadataRead-onlyRepository discovery (mandatory)
    ContentsRead-onlyCloning and scanning
    Pull requestsRead & writeRemediation pull requests
    WebhooksRead & writePush-triggered rescans

Then: note the App ID and the app slug (from its URL), generate a client secret, and generate a private key (a .pem file downloads). Finally install the app on your account or organisation — the installation URL is https://github.com/apps/<slug>/installations/new, and Plexicus needs that URL too.

Scopes vs permissions

Plexicus sends no scope parameter on the GitHub authorize URL. A GitHub App's access is defined entirely by the repository permissions above and by which repositories the App is installed on. This is also why a plain OAuth App grants less than you probably expect — another reason to prefer the App flow.

GitHub Enterprise Server

GHES is connected per connection in the UI, not through the chart. Under Connectors → SCM → GitHub, choose GitHub Enterprise Server and fill in:

  • GitHub Enterprise URL — e.g. https://ghe.corp.local
  • Then exactly one credential set; Plexicus auto-detects which mode you meant:
    • a Personal Access Token (ghp_… or github_pat_…), or
    • an OAuth App Client ID + Client Secret, or
    • a GitHub App ID + private key (PEM)

Only the OAuth App mode performs a browser redirect. It sends the user to https://<your-ghes-host>/login/oauth/authorize and comes back to https://<your-domain>/api/callback/github — so that is the Authorization callback URL to register on the OAuth App you create inside GHES. The PAT and GitHub App modes complete without leaving Plexicus and need no callback URL at all.

Plexicus reaches a GHES instance's REST API at <your-ghes-url>/api/v3 and its GraphQL API at <your-ghes-url>/api/graphql; both must be reachable from the cluster.

Chart values and Secret keys (github.com only)

Public identifiers go in your values overlay:

global:
required:
oauth:
github:
clientId: "Iv23li…" # → GITHUB_OAUTH_CLIENT_ID
appId: "1234567" # → GITHUB_APP_ID — quote it, see below
appInstallationUrl: "https://github.com/apps/<slug>/installations/new"
Quote appId — an unquoted 7-digit number renders as 3.004825e+06

Helm round-trips values.yaml through JSON, where every number becomes a Go float64, and Go's shortest-form float printer switches to scientific notation at 1,000,000. An unquoted appId: 1234567 therefore reaches the container as GITHUB_APP_ID=1.234567e+06, which no GitHub API accepts — and nothing in the render fails, so the only symptom is that App-authenticated calls quietly stop working. Write it as a string: appId: "1234567". The chart's own default is a quoted empty string, and the production and staging overlays quote theirs.

Leave freeSastToolUrl unset. Despite its name it is what the backend receives as GITHUB_OAUTH_REDIRECT_URI, and when it is empty the chart derives <scheme>://<domain>/api/callback/github — exactly the callback the SPA sends at /authorize. Set it only if you are running the Plexicus free-scan marketing funnel, in which case it must be one of the callback URLs registered on the App.

Private values go in Kubernetes Secrets:

SecretKeyValue
plexicus-fastapiGITHUB_OAUTH_CLIENT_SECRETThe App's client secret
plexicus-fastapiGITHUB_APP_PRIVATE_KEYThe .pem, base64-encoded
plexicus-workerGITHUB_APP_PRIVATE_KEYSame value — the worker needs it to open pull requests
plexicus-frontendNUXT_GITHUB_SECRET_KEYThe App's client secret
The GitHub defaults are empty, and the schema keeps them that way

clientId, appId and appInstallationUrl all default to "". Empty means "this provider is not configured" — the connector is hidden rather than shown behind a button that cannot work.

The chart's values.schema.json additionally rejects two specific literals by value: Plexicus's own GitHub App id, and Plexicus's own OAuth client id. Both once shipped as chart defaults, which pointed every self-hosted install's GitHub traffic at an App that Plexicus owns. An overlay that reintroduces either now fails at render time instead.

So there is nothing to pin off. Set clientId only when you have registered your own App, and leave it empty otherwise.

Do not write appId: 0. The schema types appId as a string, so a bare 0 fails validation, and "" — not 0 — is the value that means "no GitHub App".

Let Plexicus create the App for you

You do not have to register the App by hand. When a deployment has no GitHub App configured, the product offers to create one through GitHub's App Manifest flow: go to Settings → Integrations → GitHub, where a panel headed No GitHub App is configured on this deployment offers Create GitHub App automatically.

The button appears only when both are true: the deployment has no working App, and your account holds the backoffice-settings admin permission. Clicking it POSTs a prepared manifest to https://github.com/settings/apps/new; you review and confirm on GitHub's own page, GitHub creates the App and redirects back, and Plexicus stores the credentials and puts them straight into use — the scanning worker picks them up on its own refresh loop, so nothing needs a restart.

The generated App requests contents: read, metadata: read, pull_requests: write, issues: write and statuses: write, subscribes to push, pull_request and pull_request_review, and is created private.

What the generated App does and does not cover

Two limits are worth knowing before you choose this path over registering by hand:

  • It registers one callback URL, https://<your-domain>/api/callback/github — not the second, api.-prefixed one described in Callback URLs. Add that one by hand afterwards if you need the App-installation hand-off.
  • Webhooks are included only when your domain is publicly resolvable. Against a private or .local hostname the manifest omits them and the callback page says so — see Webhooks.

There is also no in-product way to replace an App once one is stored; the button is offered only while the deployment has none.

A stored App takes precedence over the chart values: once one exists, both the backend and the Nuxt login proxy prefer it over clientId/clientSecret from the environment — but only when the stored record carries both halves. A half-stored App never displaces a working environment pair.


GitLab

Scopes

Plexicus requests the same eight scopes for gitlab.com and for self-managed instances:

api, read_api, read_user, read_repository, write_repository, openid, profile, email

openid, profile and email identify the connecting user; read_repository and read_api cover discovery and scanning; write_repository and api cover remediation merge requests, webhooks and CI-variable reads. Granting fewer than these makes the authorization succeed and the later repository calls fail.

gitlab.com

Register the application once, at the platform level: User Settings → Applications on gitlab.com (or Admin Area → Applications for an instance-wide registration).

  • Redirect URI: https://<your-domain>/api/callback/gitlab
  • Confidential: keep checked
  • Scopes: the eight above

Then wire the Application ID and Secret in:

global:
required:
oauth:
gitlab:
clientId: "<application-id>" # → GITLAB_OAUTH_CLIENT_ID
SecretKey
plexicus-fastapiGITLAB_OAUTH_CLIENT_SECRET
plexicus-frontendNUXT_GITLAB_SECRET_KEY

Self-managed GitLab

A self-managed instance is connected per connection, in the UI — it does not use the chart-level client id or secret. Each connection carries the client credentials of an application registered on your own GitLab.

See GitLab → Self-Hosted for the full walkthrough. In short: register an application on your instance with redirect URI https://<your-domain>/api/callback/gitlab and the eight scopes above, then in Plexicus open Connectors → SCM → GitLab, choose GitLab Self-Hosted, and enter the instance URL, the Application ID and the Secret.

Because the redirect URI is the same for both, one Plexicus deployment can serve a gitlab.com connection and any number of self-managed connections at once.


Bitbucket Cloud

Bitbucket Cloud only — see the note in Supported providers.

In your workspace: Settings → OAuth consumers → Add consumer.

  • URL (homepage): https://<your-domain>

  • Callback URL: https://<your-domain>/api/callback/bitbucket_cloud

  • This is a private consumer: keep checked

  • Permissions:

    PermissionLevelNeeded for
    AccountReadIdentifying the connecting user
    RepositoriesReadDiscovery and scanning
    Pull requestsWriteRemediation pull requests
    IssuesWriteIssue creation from findings

Plexicus sends no scope parameter to Bitbucket; the consumer's permission checkboxes are what the token ends up carrying, so set them before you connect.

Expand the saved consumer to reveal its Key and Secret:

global:
required:
oauth:
bitbucket:
key: "<consumer-key>" # → BITBUCKET_OAUTH_CLIENT_ID
SecretKey
plexicus-fastapiBITBUCKET_OAUTH_CLIENT_SECRET
plexicus-frontendNUXT_BITBUCKET_CLOUD_SECRET

Gitea and Forgejo

Both are personal-access-token connectors. Neither uses OAuth, and neither needs any chart value, Secret key or callback URL. Attempting an OAuth redirect for Gitea or Forgejo is rejected by the platform with an explicit error message pointing you back at the token form.

Generate the token on your instance under Settings → Applications → Generate New Token, granting at minimum:

  • repository: read — discovery, cloning and scanning
  • user: read — Plexicus verifies the token against /api/v1/user
  • repository: write — additionally required for remediation pull requests

Then in Plexicus open Connectors → SCM → Gitea (or Forgejo) and fill in:

  • Gitea URL / Forgejo URL — origin only, e.g. https://gitea.corp.local or https://gitea.corp.local:3000. No path, no trailing slash.
  • Token — the personal access token

Click Connect; Plexicus validates the token against your instance before saving it.

Gitea OAuth environment variables

GITEA_CLIENT_ID, GITEA_CLIENT_SECRET and GITEA_REDIRECT_URI exist in the platform's configuration surface, but the Helm chart sets none of them and the connector UI does not use them. Configure Gitea and Forgejo with a personal access token, as above.


Webhooks

Push-triggered rescans require your provider to make an inbound HTTP request to your Plexicus deployment. That is the opposite direction from the OAuth redirects, and it is where self-hosted topologies commonly fall short.

Webhooks cannot work when:

  • <your-domain> is a .local name, an /etc/hosts entry, or any hostname that does not resolve on the public internet — github.com, gitlab.com and bitbucket.org cannot look it up;
  • the deployment sits behind a firewall or on a private network with no inbound path from the provider;
  • you use a self-signed or private-CA certificate that the provider will not trust.

None of this affects scanning. Manual scans and scheduled scans work normally; only push-triggered rescans are unavailable. To enable them on an evaluator, use a publicly resolvable domain — plexicus.<vm-ip>.nip.io avoids editing /etc/hosts entirely — and leave Webhook → Active checked on the GitHub App.

A self-managed provider (GitHub Enterprise Server, self-managed GitLab, Gitea, Forgejo, Azure DevOps Server) only needs to reach Plexicus across your own network, so webhooks from those are usually fine even on a private hostname.


Troubleshooting

redirect_uri_mismatch (GitHub) / invalid_redirect_uri (GitLab, Bitbucket) right after clicking Authorize

The callback URL registered with the provider is not what Plexicus sent. Compare them character by character: same scheme (https vs http), same hostname (no api. prefix for the sign-in and connector callbacks), no trailing slash, no query string. For GitHub, confirm that both callback URLs from Callback URLs are registered.

400 Bad Request at token exchange, with no provider error page

The client id is configured but its secret is not — a half-configured connector renders a working Connect button that can only fail at the exchange step. Check that the matching *_OAUTH_CLIENT_SECRET key exists in the plexicus-fastapi Secret and is non-empty:

kubectl -n plexicus get secret plexicus-fastapi \
-o jsonpath='{.data.GITHUB_OAUTH_CLIENT_SECRET}' | base64 -d | wc -c

0 means it was never set. On GitHub specifically, confirm the clientId in your overlay is the one from your own App — see Chart values and Secret keys.

The connector says Connected but no repositories appear

The token authorized successfully but lacks read access. Re-check the scopes or permissions table for your provider above; for GitHub, confirm the App is actually installed on the account or organisation that owns the repositories, not merely authorized.

Scans run but no remediation pull requests are created

Write access is missing: GitHub Pull requests: Read & write, GitLab write_repository and api, Bitbucket Pull requests: Write, Gitea/Forgejo repository write. On TFVC, pull requests are not supported at all.


Next steps