Running locally
How to run Companion Hub from a source checkout — for contributing, or for testing changes before they ship.
The Companion Hub repository uses pnpm , not Bun. It
declares packageManager: pnpm@11.5.1 and ships only pnpm-lock.yaml. This
docs site uses Bun; Hub does not. Running bun install in a Hub checkout will
not give you a working tree.
The repository is public at
github.com/companionintelligence/Companion-Hub ,
and its default branch is dev. You can clone it and run pnpm install
without a GitHub account or an access token. You need an account only to open
an issue or a pull request.
The development loop
Clone, install, bring the stack up from source, edit, test — there is no Docker image rebuild between edits.
flowchart LR
a["Clone the repository"] --> b["pnpm install"]
b --> c["cp .env.example .env.local<br/>fill in the required values"]
c --> d{"How to start"}
d -->|"Wizard"| e["./bin/cihub.cjs wizard"]
d -->|"By hand"| f["cihub setup local<br/>cihub up local"]
e --> g["Dashboard at :5005<br/>API at :5004"]
f --> g
g --> h["Edit the source"]
h -->|"backend and frontend<br/>run from source"| g
h --> i["pnpm test"]
i --> j["cihub down<br/>or cihub reset local --yes"]
click b "#install-dependencies"
click c "#create-your-env-file"
click e "#start-the-stack"
click f "#doing-it-by-hand-instead"
click i "#running-tests"
click j "#day-to-day"Prerequisites
- Docker with the Compose plugin — verify with
docker compose version. See Install Docker if you need it. - Node.js 22 or later — Hub declares
engines.node: >=22, and.nvmrcpins Node 22 - pnpm 11 or later —
engines.pnpm: >=11
The simplest way to get the right pnpm is Corepack, which reads the pinned version out of the repository’s packageManager field:
corepack enableInstall dependencies
Clone the repository (or your fork of it), then install from its root:
pnpm installThe install needs no credentials. The design files the frontend needs are copied into the repository, so no private package registry is involved.
Build packages/common before you run backend tests, because the backend resolves @ci-hub/common/validation from its dist/ folder:
pnpm --filter common buildIf you build the desktop app or run its Rust tests, you also need the GTK and WebKit development headers. On Debian and Ubuntu:
sudo apt-get install -y build-essential pkg-config libssl-dev libdbus-1-dev libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libxdo-devThe web stack does not need them.
Create your env file
A source checkout does not generate its own environment file. Copy the template and fill in the required values:
cp .env.example .env.local.env.example is a template, not a working config. Set at least the values
below in .env.local before you start the stack.
| Variable | Value |
|---|---|
ENV_FILE | .env.local — must match the file name |
ROOT_FOLDER_HOST | Absolute path for persistent data |
POSTGRES_PASSWORD | Any password; the Postgres container and the backend share it |
JWT_SECRET | A random secret, for example from openssl rand -hex 32 |
INTERNAL_IP | 0.0.0.0 binds all interfaces |
DOMAIN | The public domain used for Traefik routing |
CI_CLOUD_URL | https://hub.ci.computer for the production Portal |
Start the stack
Run the wizard
./bin/cihub.cjs wizardThe wizard needs an interactive terminal. When .env.local is missing, it enters first-run mode: it shows a step tracker, checks that Docker is available, prepares host assets, and starts the Hub. It does not create the env file for you, so create .env.local first, as described above.
For the local environment, the backend and frontend run in the foreground of the wizard’s terminal, so the wizard’s later steps — pairing with CI Cloud and installing a model — do not run. In a second terminal, run cihub register local to pair. Install models from the dashboard.
./bin/cihub.cjs is the CLI as declared in the repository’s package.json.
It runs your working-tree TypeScript directly — see Running the CLI
locally.
Open the dashboard
cihub up local runs the backend and the Vite dev server on your host, so its ports differ from the containerized stack:
| Surface | URL |
|---|---|
| Dashboard (Vite dev server) | http://localhost:5005 — Vite opens it in your browser |
| Backend API | http://localhost:5004 — Vite proxies /api here |
| PostgreSQL | localhost:6543 |
| RabbitMQ | localhost:5672 |
The containerized Hub — the desktop app, and cihub up dev or cihub up prod — serves the dashboard at http://localhost:5002 instead.
Doing it by hand instead
If you’d rather not use the wizard, the same result in two commands:
cihub setup localcihub up localcihub up local starts PostgreSQL and RabbitMQ in Docker, then runs the backend and frontend from source — no Docker image rebuild between edits. pnpm run local is the same command.
If port 5004 or 5005 is already in use by something else, the command stops with Local development port conflict. Stop the other process and run it again.
Environments
Each environment reads its own file at the repository root:
| Environment | File |
|---|---|
local | .env.local |
dev | .env.dev |
staging | .env.staging |
prod | .env.prod |
Most commands take the environment as an argument — cihub up local, cihub setup local, cihub wizard staging. Omitted, it defaults to local.
Day to day
cihub status # containers, dashboard URL, tunnel, VPN, modelscihub down # stop the stackcihub reset local --yes # wipe local runtime state for a clean-slate runcihub reset removes installed apps, runtime state, volumes, and the data folder. It does not delete .env.local, so the wizard does not re-enter first-run mode afterward. To see the first-run flow again, move .env.local aside first.
For cihub up local, cihub status prints http://localhost:5002 as the
dashboard address unless your env file sets FRONTEND_PORT or BACKEND_PORT.
Use the addresses in the table above instead.
See the CLI Reference for the full command set.
Running tests
pnpm test # unit tests, the CLI suite, and the entrypoint testspnpm test:integrationpnpm test:e2epnpm test:cli # CLI and TUI presentation testsAutomatic CI triggers are gated, so run pnpm run check:pr before you request review — nothing runs it for you on push. It runs lint, the dead-code check, type checks, the OpenAPI drift check, the tests, and the desktop crate’s Rust tests.
Working on the CLI itself? Running the CLI locally covers the tighter iteration loop.