On this page
Operating and deploying Claude Commander
This page is for whoever moves code between environments and keeps the process alive. Commander's deployment model is a per-project promotion ladder: an ordered list of environments, each a checkout of its own branch, promoted one rung at a time by the server, with each rung recorded.
Tutorial: your first promotion
The tutorial uses the reference layout: a dev worktree on its own branch and ports, promoted to prod.
- Register the project with its git path. Commander confines every path it touches to a root allowlist, so the path must sit under a configured project root.
- Define the ladder in the project's Deploy tab. Each rung names an environment, a branch, an optional pm2 process, a health URL, a migration command and its database URL environment variable. The ladder is stored as JSON in the project's settings and validated against a schema on save.
- Run Verify. The first rung checks that the source tree is clean, the target branch is fast-forwardable, and the version manifest (if present) validates.
- Read the plan. Plan lists the commits that will move, capped by
PROMOTE_PLAN_MAX, so you know exactly what a promotion carries. - Promote. The server runs the rungs in order: verify, plan, test, push, advance, install, migrate, restart, config, health. Those are the human names; the step log uses the emitted identifiers in the table below, where
pushis recorded as two steps and the fidelity rung is namedconfig. A failure halts the ladder at that rung and shows the queued commits so nothing is lost. Promote itself is an operator action in the UI; agents prepare, operators press. - Read the record. Every run is stored with per-rung timing, output tails and the identity that triggered it.
How-to
Understand each rung
| Rung | What it does | Halts when |
|---|---|---|
verify (plus verify-autoclean when autoclean runs) | tree clean (or autoclean of known-generated files, FEAT-046 autoclean), target fast-forwardable, manifest valid | dirty tree, diverged branch |
plan | lists commits to move | nothing to move |
test | runs the configured test argv in a narrowed environment | non-zero exit |
push-source, push-target | pushes the source branch, then the advanced target branch, to the remote — two recorded steps, not one | remote refuses |
advance | fast-forwards the target branch | not a fast-forward |
install | runs the install argv (default npm ci) in the target | non-zero exit |
migrate | runs the migrate argv (by default the db:migrate script, which runs node migrate.js) against the database URL named by the stage's migrate.dbUrlEnv | ledger checksum mismatch, SQL error |
restart | restarts the target pm2 process | pm2 error |
config | compares served configuration against the expected keys, the fidelity check of FEAT-034 | missing or changed keys |
health | polls the target health URL with retries and delay | no 200 within the budget |
A rollback records a rewind step, and a promotion that touches a remote records remote. Every timeout and retry count is an environment variable with a named default; none is inline.
Use the operator override
When the tree is dirty for a reason you understand, the Deploy tab offers an override (FEAT-046) if promote_allow_override is on. It needs a reason, is recorded with the run, and still performs a server-side fast-forward safety check so it can never clobber the target. Halts always show the queued commits.
Supervise with pm2
The reference deployment runs commander (prod) and commander-dev under pm2. The heartbeat panel reads pm2 jlist, shows CPU, memory, restart counts and log tails, and offers start, stop and restart per process. Two rules from the pm2 operations runbook: never restart with --update-env from a shell whose environment you do not fully trust, because pm2 stamps that environment into its saved state; and after any change run pm2 save so a host reboot resurrects the right set. Restarting the dev instance drops every live terminal on it; stage and verify changes statically first when people are working.
Supervise with Guardian and systemd
Guardian is the platform supervisor that rebuilds the pm2 set from its own manifest on a recycle. Commander reads Guardian's registry so the monitor lists the same services, and links to Guardian with a key. Services Guardian marks as systemd (a local model server, for example) are handled by health endpoint and systemctl status, with start and stop through a validated sudo argument list (FEAT-049); pm2 verbs are never sent to a systemd unit. If Guardian is not installed, Commander says so rather than guessing.
Deploy the container
The deploy/ directory holds the OCI image build and a tenant compose file. The Dockerfile pins the Claude Code CLI version as a build argument, so an image is reproducible. Tenants get one container, one database and one secrets file. Health is whatever URL the tenant's ladder stage names (see the health rung below).
Sign and check a version manifest
A version manifest describes what a release contains and is validated against deploy/version-manifest.schema.json (JSON Schema 2020-12). Releases are signed with an EdDSA key (RFC 8032, JWS per RFC 7515, key id per RFC 7638) that is separate from the agent-manifest key:
node scripts/sign-version-manifest.js deploy/version-manifest.json
node scripts/release-catalog.mjs
The verify rung refuses a manifest whose signature or schema fails. Versions follow SemVer 2.0; the app version has a single source in package.json and is served to the browser as appVersion on /api/client-config.
Run migrations by hand (dev only)
node migrate.js
Never apply SQL directly, even on dev. A ledger that lists fewer rows than the objects on disk halts the migrate rung on the next promotion, and reconciling it is manual work. The one sanctioned exception is the ledger table's own DDL, which lives in code so that a fresh database can bootstrap.
Watch the host
The heartbeat panel has five tabs: Server (load, memory, disk, CPU steal with warn and error thresholds), Commander (version, uptime, session counts), Platform (Guardian registry), Dependencies (Claude Code CLI version, pinned versions from the governed channel) and Monitor (per-process history). Thresholds and poll intervals are configuration; history is persisted to a file so a restart keeps the graph.
Read usage and cost
The usage panel folds every transcript into per-model token counts and cost using a pricing table with a PRICING_AS_OF date; a stale table is flagged after PRICING_STALE_DAYS. Rate-window meters in the header show the subscription windows. Sub-agent transcripts fold into their parent.
Reference
Ladder rung configuration keys
Each stage is one object in the ladder's ordered stages array. A ladder needs at least two stages, or an empty list to unconfigure it. Stage names must be unique.
| Stage key | Required | Meaning |
|---|---|---|
name, branch, path | yes | environment identity, its branch, and its checkout. path must be absolute. |
pm2 | no | process name to restart, as a string |
health | no | a single http: or https: URL (RFC 3986), fetched by the health rung |
migrate.dbUrlEnv | with migrate | name of the environment variable holding the target database URL. The URL itself is never stored in the ladder. |
migrate.command | no | argv array overriding the default migrate command for this stage |
manifest | no | version-manifest path for the verify rung |
Retries, delay and timeout for the health rung, and the argv for the install and test rungs, are server-level configuration rather than stage fields — see the table below.
Deploy-related environment variables
| Variable | Default | Purpose |
|---|---|---|
PROMOTE_RUN_TESTS | on | run the test rung; PROMOTE_TEST_ARGV defaults to npm,test |
PROMOTE_INSTALL_ARGV | npm,ci,--no-audit,--no-fund | install rung argv, comma-split and run with execFile, never a shell |
PROMOTE_MIGRATE_ARGV | npm,run,db:migrate | migrate rung argv (the script runs node migrate.js), comma-split, execFile |
PROMOTE_HEALTH_RETRIES, _DELAY_MS, _TIMEOUT_MS | named defaults | health rung |
PROMOTE_ALLOW_OVERRIDE | off | expose the override |
PROMOTE_VERIFY_AUTOCLEAN | off | clean known-generated files at verify |
PROMOTE_PLAN_MAX | named default | commits listed by plan |
SYSTEMD_CONTROL_ENABLED | off | allow systemd start and stop |
TRANSCRIPT_EXCLUDED_DIRS | empty | populations skipped by the scan |
Health response
There is no built-in health endpoint. The health rung fetches the URL configured on the target ladder stage, which is validated as http: or https: when the stage is saved, and treats a reachable non-error response as healthy. Timeout, retry count and the delay between retries all route through the configuration chain (PROMOTE_HEALTH_TIMEOUT_MS, PROMOTE_HEALTH_RETRIES, PROMOTE_HEALTH_DELAY_MS), and the rung fails the promotion when every attempt fails. Point a stage's health URL at whatever that environment considers proof of life. Licence-plane errors elsewhere are RFC 9457 problem documents.
Explanation
Why the ladder is server-side. A promotion run from a laptop depends on that laptop's git state, environment and attention. Running it in the server, one rung at a time, with each rung recorded, makes the run reproducible and reviewable, and lets a halt show exactly what is queued.
Why migrate is a rung. Schema and code move together or not at all. The migrate rung runs against the target database named by the rung, reads the URL from the environment rather than storing it, and halts on a checksum mismatch, so a hand-edited migration cannot slip through.
Why the override still checks fast-forward. An override exists for dirty trees, not for rewriting history. The server re-checks that the advance is a fast-forward after the override, so the worst case of a mistaken override is a halted run, not a lost commit.
Why Guardian and pm2 both matter. pm2 keeps a process alive; Guardian keeps the process set correct across a host recycle. The 2026-08-02 incident record in the repository shows what happens when the two disagree about the set: the fix was a pinned working directory in the ecosystem file and a scan exclusion, both now defaults.
Related
- Admin for secrets, auth and backups.
- Developer for migrations and the test layers the test rung runs.
- Application for what a session is.
- Reference · Best practices · Agents
You've seen the proof
Ready for a number? Scope your deployment and we'll price it against your own economics.
Get your quote →