Skip to content
GhostbranchdocsGitHub

Setting up in detail

Everything ghostbranch init shows and asks, answering it with flags, a real setup, installing the push guard on its own, checking the setup, setting up with a coding agent, and the errors you may see.

This page expands Set up a repo: what init shows at each step, how to answer it without questions, and how to check the result. Run init in a clone of the public repo, so Ghostbranch can read the repo’s name from origin and guard that checkout. Each machine you work from needs it: on a second machine, you adopt the shadow repo the first one made.

  • The shadow repo: a private copy of your public repo, named <repo>-shadow unless you choose another name. Your private feature branches live there, and its CI runs on them.
  • The overlay branch: a branch called ghostbranch in the shadow repo. It holds the settings file .ghostbranch.yml and becomes the shadow repo’s default branch. Nothing on it is ever published.
  • The push guard, if you want it: a git hook in your checkout that refuses to push feature work to the public repo by mistake.

init never writes to the public repo. If it stops partway, run it again and it carries on from where it stopped.

init reads your public repo’s default branch from GitHub, so a repo that works on master, trunk or another branch needs no change. The pair works on that branch from then on. Ghostbranch calls it the base branch, and these pages say main for it in their examples.

  • The shadow repo mirrors it under the same name. A master repo’s shadow has master, not main. The public repo’s other branches are mirrored too, but nothing Ghostbranch does targets them: features start from the base branch, the gate rebases onto it, and pull requests go into it.
  • init records it in .ghostbranch.yml, as public_branch: master, and sync.branches lists it. A main repo’s file has no public_branch line. See Configuration.
  • The output names it. init ends with Shadow repo you/project-shadow is ready for you/project on branch master., and other commands say shadow master and public master. With --json, init, status, sync, doctor, feature list, feature show and feature start give it as base_branch.

Ghostbranch doesn’t follow a renamed default branch. If the branch is renamed on GitHub after setup, every command stops with BASE_BRANCH_CHANGED: see The default branch was renamed.

Run at a terminal, init asks only what it can’t work out on its own. Press Enter to take the suggestion in brackets. A yes-or-no question says its default in words: [Y/n, Enter = yes] or [y/N, Enter = no].

Before it changes anything, init checks each step of the setup and shows a checklist: the login, the account, the GitHub app’s install, the shadow repo, the app’s access to your repos, the pair, the push guard and doctor. Each line has a mark:

Mark Meaning
check mark Done already.
· (a dot) Ghostbranch will do it.
exclamation mark It needs you. init says what to do, and waits.

A first run, with no login yet, looks like this up to the first step that needs you. The sample uses the account alex, with the public repo alex/project:

Terminal
ghostbranch init
Ghostbranch will set up private branches for a public GitHub repo.
It asks a few questions. Press Enter to accept the suggestion in [brackets].
Which public repo should get private branches? (owner/repo) [alex/project]:
No GitHub login found. Log in with GitHub now? [Y/n, Enter = yes]
To log in, open https://github.com/login/device and enter this code: WDJB-MJHT
(It is open in your browser.)
Waiting for you to approve it on GitHub. Ctrl-C cancels.
Logged in to GitHub as alex with the Ghostbranch GitHub App.
✓ Logged in as alex (from your Ghostbranch login).
✓ The token is alex's, which can set up alex/project-shadow.
! Install the Ghostbranch app on alex
Ghostbranch acts on GitHub through its app, and only on the repos you pick.
1. Open https://github.com/apps/ghostbranch-cli/installations/new
2. Choose alex, then "Only select repositories", then pick alex/project
(init creates alex/project-shadow next; the app gets it automatically)
3. Click Install, then come back here
· Ghostbranch creates the private repo alex/project-shadow.
· Ghostbranch checks the app can see alex/project once it is installed.
· Ghostbranch registers alex/project with alex/project-shadow.
· Ghostbranch runs doctor once setup is done.
Press Enter once it's installed.

At a terminal the ! steps are in bold yellow, the done steps green, and the steps Ghostbranch will do dimmed. The push guard has no line until you choose it below. If you’ve logged in before, the login question doesn’t appear.

If the shadow repo already exists, the app’s install step asks you to pick it as well as the public repo.

When you press Enter, init checks again. If the app still can’t see a repo it needs, it says which one, with a link to the install’s Configure page, where you tick it. For a repo that belongs to an organization, only an owner can install the app: a member’s install is a request, and setup continues once an owner approves it.

init checks a step that needs you twice. If you press Enter the second time and it still isn’t done, init stops instead of asking again. It names the step’s code, such as APP_NOT_INSTALLED, and changes nothing. Do the step, then run ghostbranch init again: it carries on from the checklist.

With every ! step done, the checklist shows again, and init asks the rest:

  1. “Create the private repo you/project-shadow to hold your private branches (the shadow repo)?”
  2. “Install the push guard in this checkout? It refuses pushes of feature work to the public repo.” The default is yes.
  3. “Turn on scheduled sync? A workflow in the shadow repo then syncs it every 6 hours (about 120 Actions minutes a month).” The default is no.

For a repo that belongs to an organization, question 1 asks where the shadow repo should live instead: in the organization, so teammates can use it; in your own account, so only you can; or under another name, or an existing repo to adopt.

Then it shows exactly what it will change and asks “Make these changes now?”. Once you say yes, it sets up the pair, installs the guard, runs ghostbranch doctor, and ends with the commands for your first feature.

init is safe to run again. If it stopped partway, it carries on from where it stopped. On a setup that’s already finished, it shows the checklist with every step done, asks no setup questions, and checks the setup with doctor. It still asks which repo, unless you name it, as in ghostbranch init you/project:

Terminal
ghostbranch init
the repo question, the all-✓ checklist, then:
Resuming the setup of you/project, whose shadow repo is you/project-shadow.
Nothing to change: you/project is already set up with you/project-shadow.
Checking the setup (ghostbranch doctor)…
then the doctor summary

The one question it may still ask is the push guard’s, if the guard isn’t installed in this checkout.

If init says GitHub refused to create the shadow repo with “Resource not accessible by integration”, the app isn’t installed where it needs to be. Install it as in Set up a repo, then run ghostbranch init again.

Each question has a flag, and --yes takes the defaults for the rest. With --json, --non-interactive or --yes, init never asks anything, which is how scripts and agents run it.

Flag What it answers
<owner/repo> The public repo, as the first argument.
--shadow <owner/name> The shadow repo’s name, if not <owner/repo>-shadow.
--adopt Take over a shadow repo that already exists, instead of creating one.
--guard, --no-guard Install the push guard in this checkout, or don’t.
--sync, --no-sync Turn on scheduled sync, or leave it off.
--plan Show what would change, and change nothing.

To take over a shadow repo that already exists, see Adopt a shadow repo that already exists.

This is a real init --adopt run, with the names changed. The shadow repo had been created by hand first. init mirrored the public main into it, created the overlay branch and made it the default, and audited the CI. The repo had no workflows yet, so the audit found nothing to fix. These are its two warnings, with its progress and result lines left out:

Terminal
ghostbranch init you/project --adopt
setup progress and the result left out
warning: you/project is private; Ghostbranch is meant for public repos
warning: you/project-shadow is private, so its Actions minutes are billed
  • PUBLIC_NOT_PUBLIC: the “public” repo was still private, as it was before its launch. Ghostbranch is meant for public repos, so check you named the right one. It still works on a private repo.
  • ACTIONS_MINUTES_BILLED: GitHub bills the Actions minutes of a private repo to its owner, and the shadow repo is always private. Nothing needs doing. Scheduled sync adds to the bill, which is why it’s off unless you turn it on.

Its last step suggested the push guard. That step is yours to run, never an agent’s.

If you skipped the guard during init, install it in your checkout of the public repo:

Terminal window
ghostbranch guard install

It adds a pre-push hook. When you git push feature work to the public repo by mistake, the hook refuses, and nothing is sent. An existing pre-push hook keeps running after it. ghostbranch guard status says whether it’s installed, and ghostbranch guard install --plan shows what it would change.

Run ghostbranch doctor inside the checkout. Now that there’s a pair, it checks all seven things:

Terminal
ghostbranch doctor
✓ tools git 2.51.0; ghostbranch v0.0.0-20261012143300-b5fd1e633402, commit b5fd1e633402
✓ token the app token from login works, as you; can publish to you/project: true; it has every permission the commands need
✓ app the Ghostbranch app is installed and can see you/project and you/project-shadow
! pair you/project (public) and you/project-shadow (private) are reachable, and the overlay and its config are valid; you/project is private; Ghostbranch is meant for public repos
✓ guard the push guard is installed in /home/you/project, current, and guards every remote at the public repo
✓ state workspace "default"; the records are readable, none is newer than this binary, and no gate job has been pending for over an hour
✓ ci 0 repo guard(s) the gate rewrites; secret names not checked (no workflow uses one, or the token can't list them); no workflow runs on sync's pushes
update check: off (development build)

The ! on pair is the same private-repo warning as above. With a public repo, every check passes. The app line appears with a login from the Ghostbranch app; with another token it’s skipped. ghostbranch doctor explains each check.

The ci line summarizes the CI audit. Run ghostbranch audit for the whole report: what keeps the shadow repo’s CI from matching the public repo’s, such as jobs that skip outside the public repo, or secrets the shadow repo lacks.

A coding agent can run the whole setup of a repo for you. It checks each step, does the ones it can, and stops for the few that only you can do, on GitHub. You can ask it in plain words, such as “Set up Ghostbranch for this repo.”

The agent learns how from ghostbranch guide. Working with a coding agent shows how to point it there.

  1. ghostbranch init <owner/repo> --plan --json checks every step and changes nothing. Its data.setup.steps is the same checklist a person sees at a terminal: the login, the account, the app’s install, the shadow repo, the app’s access, the pair, the guard and doctor. Each step is done, needs_human, blocked, todo or skipped. JSON output and next describes them.

  2. The next list in each result says what to run after it, and whether a person must run it (needs_human). When a step needs you, the agent gives you that step’s message and link, one at a time, then runs the same init --plan --json again to check.

  3. ghostbranch login --json --non-interactive, if you aren’t logged in. The agent starts it in the background. Its first line has the code, which the agent passes on to you, and it ends once you’ve approved:

    the first line (sample code)
    {"schema_version":"0.17","type":"device_code","user_code":"WDJB-MJHT","verification_uri":"https://github.com/login/device","expires_at":"2026-10-18T15:15:00Z"}
  4. ghostbranch init <owner/repo> --json, without --plan, once no step needs you. It does the todo steps: it creates the shadow repo and registers the pair.

  5. ghostbranch doctor --repo <owner/repo> --json, to check the setup.

Setup always uses your own Ghostbranch login. If the agent finds another token first, it doesn’t go on with it:

  • The GitHub CLI’s token: it runs the check again with GHOSTBRANCH_NO_GH=1, which turns that fallback off, and logs you in as above.
  • A token in the environment, GH_TOKEN, GITHUB_TOKEN or GHOSTBRANCH_TOKEN: GHOSTBRANCH_NO_GH doesn’t turn these off, so the agent asks you about the one that’s set, since setup needs your own login.

It never offers the GitHub CLI’s login, and it never asks you for a token.

Each happens on GitHub, in your browser, signed in as the account that owns the public repo. The agent tells you when, with the link.

  1. Approve the login. Open https://github.com/login/device, enter the code the agent gives you, and click Authorize. The code lasts about 15 minutes; if it runs out, the agent starts a new one.
  2. Install the Ghostbranch app. Open https://github.com/apps/ghostbranch-cli/installations/new. Choose the account that owns the public repo. Under Repository access, choose Only select repositories, then pick the public repo, and click Install. You don’t need to pick the shadow repo: init creates it next, and GitHub adds it to the app’s repos. If GitHub asks you to authorize the app first, click Authorize.
  3. Approve the app’s access, or a permission request, if GitHub or the agent asks. If the app can’t see a repo it needs, the agent gives you the install’s Configure page, where you tick that repo and click Save. When the app asks for a new permission, such as Checks: Read-only in v0.2.0, accept it on the same page. In an organization, only an owner can install the app or change its access: a member’s install is a request, and setup carries on once an owner approves it.

Tell the agent when each is done. It checks again before it goes on.

Afterwards: a token of its own, or your login

Section titled “Afterwards: a token of its own, or your login”

Once setup is done, the agent asks you once how it should work from then on:

  • Its own token (recommended). You run ghostbranch agents token in your own terminal. It opens GitHub’s new-token page with the permissions filled in, reads the token you paste without showing it, checks that it reaches the shadow repo with what agents need and can’t push to the public repo, and prints the line that sets GHOSTBRANCH_TOKEN. Then you restart the agent in a shell with it set. The agent can do everything up to the publish, and only you can publish. See An agent’s own token.
  • Your login. The agent keeps using your stored login, which can publish. ghostbranch doctor and ghostbranch agents status then warn AGENT_ON_PERSON_LOGIN.

When a checklist step needs you, init --plan --json writes nothing and stops with that step’s code. The agent tells you what to do for it, then checks again. The codes are AUTH_MISSING, LOGIN_EXPIRED, APP_NOT_INSTALLED, APP_CANT_SEE_REPO and TOKEN_WRONG_ACCOUNT, each explained under Errors you may see.

Two more codes can stop the agent later. SHADOW_CREATE_DENIED comes when init creates the shadow repo and GitHub refuses: you create the empty private repo yourself, and the agent adopts it. AGENT_TOKEN_REFUSED comes from ghostbranch agents token, when the token you made could publish or misses what agents need: you make a new one from its link.

AUTH_MISSINGexit 1No GitHub token was found.
Causes
  • GHOSTBRANCH_TOKEN, GH_TOKEN and GITHUB_TOKEN are unset, there is no stored login (ghostbranch login), and `gh auth token` gave nothing
What to do

A person runs `ghostbranch login`, or sets GHOSTBRANCH_TOKEN. Agents and CI use a token of their own in GHOSTBRANCH_TOKEN; the permissions each command needs are in the token table of `ghostbranch guide`. `gh auth login` also works.

This needs a person.

LOGIN_EXPIREDexit 1The stored GitHub login has expired and can't be refreshed.
Causes
  • its refresh token is past its six months
  • the login was revoked on GitHub, or the Ghostbranch app was uninstalled or its authorization removed
What to do

A person runs ghostbranch login again. An agent stops and hands this to the person; an agent with a token of its own sets GHOSTBRANCH_TOKEN, which never uses the stored login.

This needs a person.

APP_NOT_INSTALLEDexit 2The Ghostbranch GitHub App isn't installed on an account setup needs.
What to do

Give the person each `error.details.accounts[].install_url` with its account, one at a time, then run the command in next again. In an org, a non-owner's install is a request an owner approves. Never install it yourself.

This needs a person.

APP_CANT_SEE_REPOexit 2The Ghostbranch app is installed but can't reach a repo setup needs.
What to do

Give the person `error.details.configure_url` and the repos in `error.details.repos` to tick (in an org, an owner does this), then run next again.

This needs a person.

TOKEN_WRONG_ACCOUNTexit 2The GitHub token belongs to a different account than the repo's owner.
What to do

The person logs in as `error.details.owner` (`ghostbranch login`), or keeps the shadow on their own account with the `--shadow` form in next. Never switch tokens yourself.

This needs a person.

SHADOW_CREATE_DENIEDexit 1The token may not create the shadow repo.
Causes
  • a fine-grained token can't name a repo that doesn't exist yet, so creating one needs Administration write on all of the owner's repositories
  • the token lacks Administration write in the owner account or org
What to do

A person creates the empty private repo in error.details.shadow (no README, license or .gitignore), on GitHub's new-repository page or with `gh repo create <shadow> --private`, and adds it to the token's repository access (see the token table in ghostbranch guide). Then run the init --adopt command in next. Never ask for a token with wider access.

This needs a person.

PUBLIC_NOT_PUBLICwarningThe public repo is private.
What to do

Ghostbranch is meant for public repos; check you named the right repo.

ACTIONS_MINUTES_BILLEDwarningThe shadow repo is private, so its Actions minutes are billed to its owner.
What to do

Nothing is required. Scheduled sync bills at least one minute a run (every 6 hours is about 120 minutes a month); turn it off with sync.schedule: off and ghostbranch upgrade.