Orientation

What Ghost is

The product, the one idea that shapes its design, and what is deliberately not built.

Ghost is git hosting you run yourself. A user can:

  • push and pull over HTTP (with an API key as the password) or SSH (with a key on their account);
  • browse trees, files, history and branches, search code, see languages and contributors;
  • open issues and pull requests, comment, label, assign, review line by line, merge three ways;
  • work in organizations with teams and outside collaborators;
  • publish tags and releases with assets, fork repositories, store Git LFS objects;
  • get notifications and email, register webhooks, sign commits with GPG;
  • drive all of it from the GitHub CLI, gh, which thinks Ghost is a GitHub Enterprise server.

The one idea

Most git hosts keep each repository as a bare repository on a disk. Ghost does not trust local disk with anything.

Every repository's authoritative state lives in an S3-compatible bucket as a write-ahead log. The bare repositories on disk are a cache that can be deleted at any time.

A push is not real until one small object in the bucket, the repository's index, has been swapped by a compare-and-swap. A node that has never seen a repository rebuilds it by replaying the log. Losing a disk is an operational chore, not data loss.

Almost every unusual decision in the codebase follows from this: why pushes are spooled to temp files, why the API never edits a repository except through one function, why a pull request "lends" objects between caches, why tags have no table. The Storage chapter is the heart of the course.

ADR 0004 is where this was decided.

The stack

LayerChoice
WebNext.js 16, React 19, Tailwind, shadcn/ui
APINestJS, OpenAPI client generated for the web
DatabasePostgres through Drizzle
StorageAny S3-compatible bucket (RustFS locally, R2 or Tigris in production)
Code searchZoekt
DeliveryA small Go service for email and webhooks
RuntimeBun, in a Turborepo workspace

Not built yet

Knowing what does not exist saves hours of searching for it:

  • CI runners and commit statuses (issue #61). The GraphQL types exist, empty, for gh.
  • Branch protection (#62).
  • WAL checkpoints and compaction: every push reads the whole index, and replay is O(history) (ADR 0011).
  • A garbage collector for orphaned log entries (ADR 0010).
  • Running more than one API replica: caches and purges assume one (ADR 0020).

docs/roadmap.md holds the order the open issues should be taken in.

Check yourself

A node's disk is wiped while the API is running. What happens to the repositories?
Which single write makes a push real?

On this page