---
title: "VCS Integration"
type: concept
tags: [integrations, workflow, self-hosting]
created: 2026-08-10
updated: 2026-08-10
sources: ["raw/github_doc-apps-docs-content-docs-vcs-integration-mdx.md", "raw/github_doc-apps-docs-content-docs-github-integration-mdx.md"]
confidence: high
multica_version: "v0.4.22"
---

## Definition

VCS integration is how Multica links pull/merge requests to issues, mirrors CI status, and auto-completes issues on merge. GitHub is supported on both Multica Cloud and self-hosted deployments; self-hosted **Forgejo**, **Gitea**, and **GitLab** are supported only on self-hosted Multica, and a workspace can connect any combination of these providers alongside GitHub. Separately, and needing no provider configuration at all, agents open pull requests directly against a repository's own Git host using the daemon runtime's own Git credentials.

## How It Works

**GitHub integration.** A workspace owner or admin opens **Settings → GitHub**, turns on the master switch, clicks **Connect GitHub**, and authorizes an account/organization and a set of repositories via GitHub's App install flow. The GitHub App only *reads* the authorized repositories' PR events — it never pushes commits, comments, or status checks. Three sub-switches control behavior: **PR sidebar** (show linked PRs on the issue), **Co-authored-by** (adds `Co-authored-by: multica-agent <github@multica.ai>` to agent commits), and **Auto-link PRs** (detect issue identifiers). A fourth item, **PR card → CI & mergeability**, pulls an authenticated GitHub API snapshot per linked PR to show CI status and mergeability, and requires `GITHUB_APP_ID` and `GITHUB_APP_PRIVATE_KEY` to be configured — without them, PRs still auto-link and merge-to-Done still works, just without the CI/mergeability display.

**Self-hosted Git providers (Forgejo/Gitea/GitLab).** This section only appears once a server operator sets `MULTICA_VCS_INTEGRATION_ENABLED=true` and a `MULTICA_VCS_SECRET_KEY` (base64, 32 bytes) on a self-hosted deployment; it is not offered on Multica Cloud. Unlike GitHub's App model, each of these providers is connected per-workspace with an access token (Forgejo/Gitea: Settings → Applications; GitLab: a personal/group/project token with `read_api`) plus a registered webhook — both the token and webhook secret are encrypted at rest. Connecting shows a webhook URL and a webhook secret **once**; the operator manually registers the webhook on the repository/organisation/group in the provider's UI (Forgejo/Gitea: trigger on Pull Request + Commit Status; GitLab: Merge request events + Pipeline events). Every webhook delivery is authenticated against the stored secret.

**Linking a PR to an issue.** Put the issue identifier (e.g. `MUL-123`) in the branch name or PR/MR title — matching is case-insensitive and scoped to the current workspace's issue prefix, and one PR can link to multiple issues. If the identifier only appears in the body, a GitHub-style close intent is required: `Closes MUL-123`, `Fixes MUL-123`, or `Resolves MUL-123` (self-hosted providers use the same closing-keyword convention). A reference-only mention like "Related to MUL-123" does not count as a working PR link, and commit messages / PR comments never trigger linking.

**Merge-to-Done.** A merged PR only moves its issue to `Done` when *all* of these hold: (1) at least one merged linked PR used a close intent immediately followed by the identifier (`Closes login MUL-123`, with words in between, does not count); (2) the issue has no other working PR still `Open` or `Draft`; (3) the issue is not already `done` or `cancelled`. Titling or branching a PR with just `MUL-123` (no close intent) links it but never auto-completes the issue on its own, and a PR closed without merging never completes the issue.

**What gets mirrored** on the issue's PR sidebar: repository, number, title, author, state (`Open`/`Draft`/`Merged`/`Closed`), added/deleted lines and changed-file count, CI status (all passed with a count, some failed naming the checks, or in-progress — "no checks configured" is never shown as passing), and mergeability (mergeable only on a clean GitHub-reported merge state, or conflicting/blocked/behind). If GitHub is briefly unreachable, the card keeps the last snapshot and marks it stale rather than going blank; merged/closed PRs stop showing CI or mergeability.

**Agents opening PRs.** This needs no provider configuration in Multica at all: agents check out repositories inside the daemon runtime and push branches / open PRs using whatever Git credentials the runtime host already has (SSH deploy key, a token in the host's Git credential helper, etc.). Repository checkout works with any Git URL, so any provider works here even without a VCS integration connected.

## Key Parameters

- `MULTICA_VCS_INTEGRATION_ENABLED` — self-hosted-only master switch for Forgejo/Gitea/GitLab; the section stays hidden until set `true`.
- `MULTICA_VCS_SECRET_KEY` — base64-encoded 32-byte key encrypting self-hosted provider tokens and webhook secrets; required alongside the enable flag.
- `MULTICA_PUBLIC_URL` — lets Multica show a ready-to-paste webhook URL for self-hosted providers; without it you must prepend your own origin to the shown webhook path.
- `GITHUB_APP_SLUG`, `GITHUB_WEBHOOK_SECRET`, `FRONTEND_ORIGIN` — required for the GitHub connect button and webhook endpoint to work at all.
- `GITHUB_APP_ID`, `GITHUB_APP_PRIVATE_KEY` — required only for the CI/mergeability snapshot; PR mirroring and merge-to-Done work without them.
- Feature switches per GitHub workspace: GitHub integration (master), PR sidebar, Co-authored-by, Auto-link PRs.
- Issue identifier format: `MUL-123`-style, workspace-prefix-scoped, case-insensitive match in branch/title; body mentions need a close-intent keyword.

## When To Use

- Connect **GitHub** when repositories live on GitHub, for either Cloud or self-hosted deployments.
- Connect **Forgejo/Gitea/GitLab** only on a self-hosted Multica deployment reaching an internal Git instance — this path does not exist on Cloud.
- Rely on **agents opening PRs directly** whenever the goal is just "let the agent push code and open a PR" without needing PR-linking, CI mirroring, or merge-to-Done in Multica's UI.
- Use a **close-intent keyword** (`Closes MUL-123`) whenever you want the issue to auto-complete on merge; a bare identifier in the title/branch is enough only to establish the link, not to close the issue.

## Risks & Pitfalls

- A webhook secret (self-hosted providers) or App private key is shown/generated once — losing it means reconnecting.
- `Closes login MUL-123` (words between the keyword and identifier) does **not** trigger merge-to-Done — the identifier must immediately follow the close-intent keyword.
- A body-only mention without a close keyword (e.g. "Related to MUL-123") never shows as a working PR and never triggers Done.
- Disconnecting GitHub in Multica does not uninstall the App on GitHub's side — new events stop flowing but the App keeps running until removed from GitHub's own installation settings.
- Without `GITHUB_APP_ID`/`GITHUB_APP_PRIVATE_KEY`, the PR card silently shows no CI/merge state — this is a graceful degradation, not an error, and easy to mistake for a broken integration.
- Mismatched webhook secrets between GitHub and the API produce `401 invalid signature` on every delivery.
- "PR sidebar" off only hides the display block; it does not disconnect anything or stop mirroring underneath.

## Related Concepts

- [[concepts/communication]] — comments and inbox notify on issue-status changes triggered by merges.
- [[concepts/chat-integrations]]
- [[concepts/self-hosting]]
- [[concepts/configuration]]

## Sources

- raw/github_doc-apps-docs-content-docs-vcs-integration-mdx.md
- raw/github_doc-apps-docs-content-docs-github-integration-mdx.md
