
Company News
Free Business Plan Upgrades for Open Source Maintainers
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.
CLI for bidirectional sync between a private monorepo and a public OSS mirror
A CLI for bidirectional sync between a private canonical monorepo and a public OSS mirror. Manages the full lifecycle of external contributions: validation, merge, sync, and attribution.
Managing OSS contributions when your canonical repo is private is painful. You need to:
This typically means hundreds of lines of YAML spread across multiple workflows, with no tests, no type safety, and failures that manifest as PRs stuck in weird states.
ossbridge encapsulates this complexity in a single, testable CLI. Your workflows become thin declarations. The state machine is explicit. Failures are clear.
Create ossbridge.json at the root of your OSS package:
{
"schema": "ossbridge/v1",
"privateRepo": "yourorg/private-monorepo",
"ossRepo": "yourorg/oss-repo",
"ossPath": "packages/mylib",
"maintainers": ["@yourorg/maintainers", "alice", "bob"],
"triggerComment": "/run-private-tests",
"requiredChecks": ["oss-quick-ci"],
"auth": {
"privateToken": "OSSBRIDGE_PRIVATE_TOKEN",
"ossToken": "OSSBRIDGE_OSS_TOKEN"
}
}
At the private repo root, create a ref that points to the OSS package:
{
"schema": "ossbridge/v1",
"ref": "packages/mylib"
}
The config lives in the OSS package because the OSS repo can only see files within its folder. The private repo uses a ref so the CLI knows where to find it.
The auth block specifies which environment variable names the CLI reads at runtime. Add matching GitHub secrets to both repos:
| Token | Scopes |
|---|---|
OSSBRIDGE_PRIVATE_TOKEN | repo |
OSSBRIDGE_OSS_TOKEN | repo, write:discussion |
Wire up your GH action workflow yaml files to use the cli. This will be company and project specific in most cases, but see example-monorepo/ for a complete working setup with all workflow files, configs, and a step-by-step setup guide.
A contribution moves through eight steps across both repos:
Contributor opens a PR on the OSS repo. Standard CI runs (lint, tests) on OSS project without access to secrets.
Maintainer triggers validation by commenting the configured trigger phrase (e.g. /run-private-tests). The dispatch-pr command (OSS repo) verifies the commenter is a maintainer, confirms required checks have passed, records the PR's HEAD SHA, labels the PR ossbridge:accepted, and dispatches an ossbridge-import event to the private repo.
Private repo imports the PR. The import-pr command (private repo) fetches the PR's commits, remaps file paths from OSS root into the monorepo subtree (/ → {ossPath}/), creates a branch ossbridge/{pr-number}, and opens a PR titled [OSS #{n}] {title} against private main. The OSS PR is labeled ossbridge:validating.
Full CI runs in the private repo with secret access — integration tests, e2e, etc.
Results are reported back. The report-result command (private repo) reads the CI conclusion, updates the state comment on the OSS PR, and sets ossbridge:validated or ossbridge:failed.
Maintainer reviews and merges the private PR. Before merge completes, verify-merge (private repo, wired as a required status check) confirms the OSS PR hasn't changed since validation (SHA match), is still open, and has the ossbridge:validated label.
Changes sync to OSS. On push to private main, sync-downstream (private repo) identifies commits touching the subtree, remaps paths back ({ossPath}/ → /), and pushes to OSS main. Original author attribution is preserved — git blame shows the contributor on both repos.
OSS PR closes automatically. On push to OSS main, close-synced-prs (OSS repo) finds PRs whose changes have landed, posts a thank-you comment with commit links, removes all ossbridge:* labels, and closes the PR.
PRs move through states tracked via GitHub labels:
[opened] → [ossbridge:accepted] → [ossbridge:validating] → [ossbridge:validated] → [closed]
↘
[ossbridge:failed]
All commands read ossbridge.json from the repository root by default. Use --config <path> to override.
dispatch-prRuns in: OSS repo | Trigger:
issue_comment
Preconditions (all must hold or exits 1):
triggerComment from configmaintainers list (supports @org/team slugs)requiredChecks have passed on the PR's HEADossbridge:* label (prevents double-dispatch)Effects:
ossbridge:accepted labelossbridge-import event to private repo with {ossPrNumber, ossPrSha} payloadExit codes: 0 dispatched, 1 precondition failed
import-prRuns in: Private repo | Trigger:
repository_dispatchtypeossbridge-import
Preconditions:
--force)Effects:
ossbridge/{oss-pr-number} in private repo[OSS #{n}] {title} against private main, body contains ossbridge metadata and file list with remapped pathsossbridge:validating label on OSS PRFlags: --force re-import even if private PR exists
Exit codes: 0 private PR created/updated, 1 import failed
report-resultRuns in: Private repo | Trigger:
workflow_runonossbridge/*branches
Preconditions:
Effects:
ossbridge:validated or ossbridge:failed label (replaces previous ossbridge label)Flags: --passed / --failed override the CI conclusion (for manual recovery)
Note: skipped CI conclusion is treated as passed.
Exit codes: 0 result reported, 1 could not determine result or post status
verify-mergeRuns in: Private repo | Trigger: required status check (PR check or merge queue)
Preconditions:
Checks (any failure exits 1 and blocks merge):
ossbridge:validated label is present (not ossbridge:failed, not ossbridge:validating)Effects on failure:
Exit codes: 0 safe to merge, 1 not safe
Wire this as a required status check to prevent merging stale validations.
sync-downstreamRuns in: Private repo | Trigger:
pushtomain
Effects:
ossPath{ossPath}/* → /*, preserves author attributionmainExit codes: 0 sync completed (or nothing to sync), 1 sync failed
close-synced-prsRuns in: OSS repo | Trigger:
pushtomain
Effects:
ossbridge:validated labelmain (handles both regular and squash merges via commit comparison)ossbridge:* labelsFlags: --pr {n} close a specific PR
Exit codes: 0 PRs closed (or none to close), 1 failed to close
statusRuns in: Either repo | Trigger: manual
Inspect PR state for debugging. Reads state from OSS PR comments and labels, discovers private PR by branch name if no state exists.
bunx ossbridge status --pr 42
OSS PR #42: Add custom validators
State: open
SHA: abc123def456
Phase: validating
Accepted by: alice at 2025-01-15T10:30:00Z
Private PR: #17 (https://github.com/yourorg/private-monorepo/pull/17)
Labels: ossbridge:validating
Validation started: 2025-01-15T10:30:12Z
{
"schema": "ossbridge/v1",
"privateRepo": "yourorg/private-monorepo",
"ossRepo": "yourorg/oss-repo",
"ossPath": "packages/mylib",
"maintainers": ["@yourorg/maintainers", "alice", "bob"],
"triggerComment": "/run-private-tests",
"requiredChecks": ["oss-quick-ci"],
"mergeMethod": "squash",
"squashCommitIncludesHistory": true,
"labels": {
"accepted": "ossbridge:accepted",
"validating": "ossbridge:validating",
"validated": "ossbridge:validated",
"failed": "ossbridge:failed"
},
"auth": {
"privateToken": "OSSBRIDGE_PRIVATE_TOKEN",
"ossToken": "OSSBRIDGE_OSS_TOKEN"
},
"notifications": {
"validationStarted": "## Private Validation Started\n\n...",
"validationPassed": "## Validation Passed\n\n...",
"validationFailed": "## Validation Failed\n\n{failureDetails}",
"prClosed": "## Merged!\n\n..."
}
}
| Field | Type | Required | Description |
|---|---|---|---|
schema | string | yes | Must be "ossbridge/v1" |
privateRepo | string | yes | owner/repo format |
ossRepo | string | yes | owner/repo format |
ossPath | string | yes | Path within private repo that maps to OSS root |
maintainers | string[] | yes | GitHub usernames or @org/team slugs authorized to trigger validation |
triggerComment | string | yes | Comment text that triggers validation |
requiredChecks | string[] | yes | Status checks that must pass before dispatch |
mergeMethod | string | no | "squash" (default), "merge", or "rebase" |
squashCommitIncludesHistory | boolean | no | Include original commit messages in squash body (default: true) |
labels | object | no | Custom label names (defaults: ossbridge:accepted, etc.) |
auth.privateToken | string | yes | Env var name for private repo token |
auth.ossToken | string | yes | Env var name for OSS repo token |
notifications | object | no | Custom notification templates (see variables below) |
Notification template variables:
| Variable | Description |
|---|---|
{ossPrNumber} | OSS PR number |
{privatePrNumber} | Private PR number |
{privatePrUrl} | URL to private validation PR |
{failureDetails} | CI failure logs |
{triggerComment} | The configured trigger comment |
{privateCommitUrl} | URL to canonical commit |
{ossCommitUrl} | URL to synced commit |
{maintainer} | Username who triggered validation |
--config <path> flagossbridge.json in current working directoryossbridge.json in repository root (detected via git)Ref configs are automatically followed to load the actual configuration.
Each repo gets an ossbridge.yml workflow. The OSS repo handles inbound PRs (dispatch-pr on comment, close-synced-prs on push to main). The private repo handles import, validation, and sync (import-pr on dispatch, report-result on workflow completion, sync-downstream on push to main). A separate ossbridge-verify.yml wires verify-merge as a required check.
Minimal OSS repo workflow:
name: ossbridge
on:
issue_comment:
types: [created]
push:
branches: [main]
jobs:
dispatch-pr:
if: github.event_name == 'issue_comment' && github.event.issue.pull_request
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- run: bunx ossbridge dispatch-pr
env:
OSSBRIDGE_PRIVATE_TOKEN: ${{ secrets.OSSBRIDGE_PRIVATE_TOKEN }}
OSSBRIDGE_OSS_TOKEN: ${{ secrets.OSSBRIDGE_OSS_TOKEN }}
See example-monorepo/ for the complete set of workflows for both repos, including the step-by-step setup guide.
| Failure | Behavior |
|---|---|
| Maintainer not authorized | dispatch-pr exits 1, posts explanatory comment |
| Required checks not passed | dispatch-pr exits 1, posts which checks are missing |
| PR already in workflow | dispatch-pr exits 1 (duplicate-dispatch guard) |
| OSS PR closed during validation | verify-merge exits 1, blocks merge |
| OSS PR updated after validation | verify-merge exits 1, posts "Validation Invalidated" with SHA diff |
| Private CI fails | report-result sets ossbridge:failed label, posts failure details |
| Sync conflict | sync-downstream exits 1 |
| Missing auth token | All commands exit 1, message indicates which env var is missing |
# Force re-import (clears existing private PR)
bunx ossbridge import-pr --force --pr 42
# Manually mark as passed
bunx ossbridge report-result --passed --pr 42
# Manually close a PR that landed
bunx ossbridge close-synced-prs --pr 42
FAQs
CLI for bidirectional sync between a private monorepo and a public OSS mirror
We found that ossbridge demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.