Federation & Cross-Instance PRs
Connect two self-hosted OpenCodeHub instances so contributors on one can fork, contribute, and open pull requests against repositories hosted on another.
Federation lets a user on instance A fork a repository hosted on instance B, push branches back to B, and open cross-instance pull requests whose head branch lives on A's fork. B pulls the head from A's fork URL, creates a normal PR (with headRepositoryId unset), and the usual review and merge pipeline applies.
Table of Contents
- Overview
- How It Works
- Setting Up Federation
- Forking a Repository from Another Instance
- Contributing Back
- Cross-Instance Pull Requests
- Permissions
- SSRF Protection
Overview
The two instances interoperate over HTTP. Instance A talks to B's REST API using a Personal Access Token belonging to the contributing user on B. All git traffic flows over the normal smart-HTTP protocol using basic auth with that PAT.
┌─────────────────┐ import (clone URL + PAT) ┌─────────────────┐
│ Instance A │ ───────────────────────────▶ │ Instance B │
│ bob/fedbase │ │ alice/fedbase │
│ (fork) │ ◀─────────────────────────── │ (upstream) │
│ │ push-upstream (git push) │ │
│ │ ───────────────────────────▶ │ │
│ │ federation/open-pull │ external-pulls │
└─────────────────┘ ◀─────────────────────────── └─────────────────┘
How It Works
- Import (fork): On instance A, import
https://B/alice/fedbase.gitand authenticate with a PAT for the user on B. A detects that the source is another OpenCodeHub instance (GET /api/instance) and records the relationship asforkedFromUrl. - Contribute back: A can push any branch on its fork back to B (
federation/push-upstream). B's existinggit-receive-packauthorizes the push viacanWriteRepo— B controls who may contribute. - Cross-instance PR: A calls
federation/open-pullon B. B validates the fork URL (SSRF check), fetches the head branch from A's fork into the B repo, computes diff stats, and creates a PR whose head lives on A's fork. - Review & merge: The PR is a normal PR on B. Reviews, approvals, CI, and the merge queue all apply. Merging fetches the head ref and merges it into the base branch.
Setting Up Federation
Federation is enabled out of the box. Two environment variables control SSRF behavior:
| Variable | Purpose |
|---|---|
FEDERATION_ALLOW_LOCALHOST |
Set true to allow fetching from localhost/loopback URLs. Only for two-instance testing on a single host or a trusted private network. Never enable in production. |
The upstream instance does not need any special config beyond the normal collaboration settings: the A user simply needs read access (to see the repo) and write access (to push branches and open PRs).
Forking a Repository from Another Instance
On instance A, use the normal Import Repository flow:
- Paste the upstream clone URL, e.g.
http://instance-b.local/swadhinbiswas/fedbase.git. - Enter the auth username and a PAT for the user on instance B (the username must match the B account, e.g.
bob). - Complete the import. Instance A records
forkedFromUrland stores an encrypted mirror PAT so it can push back later.
For cross-instance PRs the source URL must be fetchable by B — the fork must be on a host B can reach, and the fork repo must be readable by B (public, or accessible with the embedded PAT).
Contributing Back
From the fork's repository page on A, the Federation panel offers Push branch to upstream:
- Pick a branch on the fork.
- A pushes
branch:branchto the upstream clone URL, authenticating with the stored B user's PAT.
B's permission model decides whether the push succeeds — the B user must be a collaborator (or the repo must allow external writes).
Cross-Instance Pull Requests
From the same Federation panel, Open cross-instance PR:
- A calls
POST /api/repos/{owner}/{repo}/external-pullson B (server-to-server) with the fork URL, head branch, base branch, title, and body. - B validates the fork URL, fetches the head into the B repo, computes additions/deletions/changed files, and creates the PR.
- The PR's head branch resolves to the fetched ref on B, so the diff, comments, and merge all work normally.
Endpoints
| Endpoint (on B) | Purpose |
|---|---|
POST /api/repos/{owner}/{repo}/external-pulls |
Create a PR whose head is fetched from an external fork URL |
GET /api/instance |
Instance metadata probe used for detection |
Permissions
- Import on A: requires a valid PAT for the B user.
- Push upstream: B's
git-receive-packrequires the B user to have write access to the repo. - Open a cross-instance PR: B's
external-pullsendpoint requires the caller to have read access plus either write access to the repo, or the repo must have Allow external pull requests enabled (repo → Settings → Federation). - Merge: normal PR merge gates apply (approvals, required checks, branch protection).
SSRF Protection
Every server-initiated fetch (import on A, external-pulls on B) runs through validateGitCloneUrl, which blocks private ranges, cloud metadata endpoints, and non-HTTP(S)/git/ssh schemes. The localhost bypass is only available when FEDERATION_ALLOW_LOCALHOST=true, and callers gate it explicitly.
Tokens embedded into fetch/push URLs are used transiently and never persisted.