Ghostbranch
Open source · local-first CLI · beta

Private branches for public repos.

Build unreleased features in a private shadow of your open-source repo, running the same CI. Publish with one gated command, after a leak scan and with a person’s approval.

ghostbranch feature start my-feature
work privately
ghostbranch gate my-feature
rebase, CI, leak scan
ghostbranch promote my-feature
publish as a PR
The problem

On GitHub, a public repo can’t have a private branch.

Teams building unannounced features on an open-source project end up running a private mirror by hand. That works until someone makes a mistake.

  1. Every branch is public.

    Anyone can see a branch pushed to a public repo, and its CI runs, too.

  2. Forks stay public.

    A fork of a public repository is always public, so a fork can’t keep anything private.

  3. Hand-run mirrors leak.

    A push to the wrong remote, a committed secret, or CI that has drifted from the real repo: the feature goes out early, or arrives broken.

How it works

Sync brings public changes in. Only promote goes out.

  1. Work privately

    Each feature lives in a private shadow repo that mirrors your public one. Its CI runs there just as it would on the public repo.

  2. Gate it

    Rebase on public main, run the full CI, and scan every commit for secrets, internal files and private notes.

  3. Promote it

    Publish exactly what was scanned as a normal public pull request. A person approves it with a one-time code typed at their own terminal.

Features

Built so that nothing leaks by accident.

leak gate

Promote is the only way out

Every commit headed for public is scanned, both tree and message, using gitleaks’ default rules. It also catches .ghostbranch/ files, BEGIN-INTERNAL blocks and any names you list in block_patterns. A person can waive a finding by its id, but no flag skips the scan.

CI parity

Your real workflows, privately

CI runs in your shadow repo exactly as it would on the public one, without adding branches or runs to the public repo’s history.

history modes

Choose what history goes public

Keep every commit, squash them into one, or publish curated groups.

  • preserve
  • squash
  • curated
push guard

No wrong-remote pushes

A git hook in your checkout stops private work from being pushed to the public remote by mistake.

ghostbranch guard install
doctor

Names the exact fix

Checks your tools, token, repo pair, guard and state, and tells you which command fixes each problem.

token the app token from login works, as you
guard its hook was written by another version of Ghostbranch
fix: ghostbranch guard install
JSON contract

Agent-ready by default

Every command can return a stable, versioned JSON envelope containing ok, data and a next step, which says whether a person is needed.

For AI agents

Agents run the loop. People publish.

Claude Code, Codex and other agents can start, gate and plan a feature through the JSON contract. Publishing waits for a person: a one-time code typed at their own console (6 characters, valid for 5 minutes, 3 tries), or an agent grant they made earlier. Run ghostbranch guide to print the shipped agent guide.

agent · ghostbranch promote new-login --json
{
  "schema_version": "0.15",
  "command": "promote",
  "ok": false,
  "exit_code": 5,
  "data": { … },
  "next": [{
    "command": ["ghostbranch", "apply", "20261012T093000Z-4f2a9c"],
    "why": "publish the plan: a person runs this in their own terminal and types the code it shows (or allows agents first with ghostbranch agents allow)",
    "needs_human": true,
    "needs_input": []
  }],
  "error": {
    "code": "APPROVAL_REQUIRED",
    "message": "publishing needs a person to type a code at a console, or an agent grant (ghostbranch agents allow); plan 20261012T093000Z-4f2a9c is saved"
  }
}
you · your own terminal
ghostbranch apply 20261012T093000Z-4f2a9c
# a summary of what goes public, then Archive or Keep
To confirm, type this code and press Enter: K7QZM4
Leave it empty, or type n, to cancel.
K7QZM4
Published new-login as PR #12: https://github.com/you/project/pull/12
Install

One Go binary for macOS, Windows and Linux.

Homebrew

brew install ghostbranch-labs/tap/ghostbranch

Placeholder: the Homebrew tap isn’t published yet.

Scoop / winget

scoop bucket add ghostbranch <BUCKET_URL>
scoop install ghostbranch
or winget
winget install <WINGET_ID>

Placeholder: the Scoop bucket and winget package aren’t published yet.

npm

npm install -g ghostbranch
also pnpm, yarn and bun

Placeholder: the npm package isn’t published yet.

Install script

curl -fsSL <INSTALL_SCRIPT_URL> | sh

Placeholder: the install script isn’t published yet.

Go

go install github.com/ghostbranch-labs/ghostbranch/cmd/ghostbranch@latest

Works once the repository is public.

Then run ghostbranch init in your repo. It asks a few questions and sets everything up.

  1. Public repo (pre-filled from origin)
  2. Log in with GitHub (if there’s no token yet)
  3. Where the private shadow repo lives, or create it for you
  4. Install the push guard in this checkout?
  5. Turn on scheduled sync?

It asks only what it can’t work out on its own. After a summary and a final confirmation, it installs the guard, runs doctor and walks you through your first feature. Flags or --json skip the prompts.

Trust & security

Your machine, your GitHub account. Nothing else is involved.

Local-first
It runs on your machine and works with repos in your own GitHub account.
No servers, no telemetry
There are no Ghostbranch servers in the loop, and nothing phones home.
Scoped GitHub App
Login uses a GitHub App that can only access the repos you pick.
Keychain storage
Tokens stay in your OS keychain, never in a dotfile.
Human-approved publishing
Nothing goes public without a one-time code typed at a console, or an agent grant a person made earlier.
Open source
Apache-2.0. Read the code on GitHub.
Support

Support Ghostbranch

Ghostbranch is free and open source. Sponsoring it pays for the time spent building and fixing it, the CI that tests every change, the website’s domain and, later, code signing, so your computer can tell that a download really came from us. If it saves you work, please consider chipping in.

Sponsor on GitHub

Sponsorships go through the personal GitHub Sponsors page of Ayush (@ayushguptax), who builds and maintains Ghostbranch, until Ghostbranch’s organisation finishes its tax setup. Every penny goes into Ghostbranch.

Sponsors

No sponsors yet. Be the first.