On this page
Administering Claude Commander
This page is for the person who owns an instance: installs it, decides how people log in, keeps its secrets, and gets it back when something breaks. Commander is one Node.js process (server.js) plus a PostgreSQL schema named commander, supervised by pm2 in the reference deployment or by a container in the Docker deployment.
Tutorial: first install to first login
This walk-through assumes a Linux host with Node.js 20 or newer, PostgreSQL 14 or newer, and the Claude Code CLI on the PATH.
- Clone and install. Clone the repository and run
npm ci. The lockfile is authoritative; the promotion ladder uses the same command. - Create the database. Create an empty database and a role that owns it. Commander creates the
commanderschema itself. - Externalise secrets. Copy
.env.exampleand fill it, or, in the reference deployment, keep secrets in asecrets.envfile outside the working tree with mode0600. The process config reads them with arequired()helper and refuses to start when a value is stillREPLACE_ME. The minimum set is the database URL, the encryption key (32 bytes hex, used for AES-256-GCM at rest per NIST SP 800-38D), the TLS key and certificate paths, and the initial app password. - Apply migrations. Run:
node migrate.js
The ledger table commander.schema_migrations records each file with a SHA-256 checksum. This is the only sanctioned path for schema change; hand-applied SQL causes the ledger to drift and the next promotion to halt.
- Start the process. With pm2:
pm2 start ecosystem.config.js
pm2 save
The HTTP listener defaults to port 3200 and the WebSocket listener (RFC 6455) to 3201, each with its own TLS. Both bind addresses and ports are environment variables.
- Check it is up. The root URL serves the application shell once the process is listening.
GET /api/auth/checkanswers without a session and reports the active auth mode, whether SSO is enabled and whether the local password form applies — it is the endpoint the login gate itself uses.GET /api/client-configservesappVersionalong with everything the browser renders from. Migration state is read withnode migrate.js, which prints the ledger; it is not exposed over HTTP. - Log in. Open the site, enter the app password. The password is stored as an argon2id hash (RFC 9106). A successful login sets the
commander_tokencookie; automation may send the same value in anx-auth-tokenheader instead. - Rotate the bootstrap password from the profile menu. Rotation revokes every other session for that identity.
How-to
Choose an authentication mode
The setting auth_mode selects how people prove who they are. It is part of the login seam, which means a wrong value can lock everyone out; change it deliberately and keep the lockout runbook open.
| Mode | What happens | When to use |
|---|---|---|
password | Single operator password, argon2id at rest, opaque session tokens hashed with SHA-256 in commander.auth_sessions. | Personal and small-team hosts. |
oidc | Login redirects to DataShield Auth. Commander discovers the provider (RFC 8414), runs authorization code with PKCE (RFC 6749, RFC 7636, RFC 8252 for the CLI path) or accepts a broker-issued single-use code at /auth/sso/exchange, verifies the token locally against the provider's JWKS (RFC 7517). auth_oidc_flow chooses auto, code or broker. An email or domain allowlist narrows who may enter. | Teams and Enterprise with SSO. |
Whatever the mode, Commander refuses a WebSocket upgrade from an origin outside WS_ALLOWED_ORIGINS and refuses embedded-scope tokens on the terminal channel.
Handle a step-up challenge
Some settings are protected by a step-up guard. Writing them without proof of the current password returns 428 Precondition Required; a wrong proof returns 403; a malformed request returns 422. The proof travels in the x-current-password header of the same request. The guarded keys are the login-seam keys: app_password, auth_mode, the auth_oidc_* family and mcp_auth_base_url. Nothing in the product, and no agent, should route around a 428; it exists so that a stolen session cookie alone cannot change how people log in.
Recover from a lockout
Follow the login-lockout runbook shipped in the repository (docs/RUNBOOK-LOGIN-LOCKOUT.md). In outline: confirm which instance is locked, read the current auth_mode from the settings table with psql, set a fresh app password through the documented reload window rather than by editing pm2 environment in place, and record the reset. Any password an agent creates during recovery must be stored in the Password Vault in the same step:
node scripts/vault.js set --instance prod --name "Commander PROD login" --username operator --generate
The vault CLI prints a generated secret exactly once.
Keep credentials in the Password Vault
The vault (FEAT-056) is the operator's known place for every credential the instance or its agents create. Entries live in commander.vault_entries; the secret column is an AES-256-GCM blob under the instance key, the metadata columns are plain so the list can be searched. The UI lists entries masked, reveals on demand with an auto-hide timer, and stamps every reveal on the row. The same library serves the CLI:
node scripts/vault.js list --instance dev
node scripts/vault.js get --instance dev --name "<entry>" --reveal
node scripts/vault.js set --instance dev --name "<what it unlocks>" --secret-stdin < secret.txt
node scripts/vault.js delete --instance dev --name "<entry>"
set upserts by name, case-insensitively, so an agent retry is idempotent. Password length and the reveal auto-hide window are live tunables.
Manage third-party integrations
External Integrations (FEAT-051) stores provider credentials for services such as hosting, DNS or model providers in commander.external_integrations, encrypted with the same key. Each provider type is a descriptor that drives the editor form, the masking rules and a health probe that tells you a token is dead before an agent finds out. Import an existing environment file once with scripts/import-secrets.js; after that, edit through the profile menu. A revealed secret is audited but not step-up gated; treat the session cookie as the guard.
Manage Anthropic accounts
Accounts (FEAT-019) hold one or more Anthropic credentials, OAuth or API key. Personas name the account they run on, and the session breadcrumb shows it. OAuth re-authentication opens a relay flow bound to a flow id so an expired or reused code fails loudly rather than as a timeout.
Apply settings and live tunables
The configuration chain has three links and one direction: an environment variable is the declared default, a row in commander.settings overrides it, and /api/client-config serves the browser what it needs. Settings are edited in the Settings panel; the login-seam keys need step-up. The Advanced tab shows the tunables registry (FEAT-055): 29 keys such as terminal size limits, sweep intervals and list caps, each with its source (env, settings or default), validated on write and on read, applied live without a restart. A blank value restores the default.
Back up and restore
scripts/backup-dbs.sh dumps each instance database on a schedule you set. The dump contains the ciphertext of vault and integration secrets; the encryption key is not in the database, so keep the key file in your secrets store with the same retention. To move an instance, restore the dump, install the same key, run node migrate.js (a no-op when the ledger matches) and start the process. scripts/rotate-encryption-key.js re-encrypts every secret column under a new key in one transaction.
The same script also bundles the project git repositories, so a restore brings back both the database state and the working repos. Destination root, the space-separated database list, the projects directory and a retention window in days are all environment variables with named defaults, and the script is idempotent — safe to run repeatedly from cron or a timer.
Turn on entitlement enforcement
Licences are Ed25519-signed JWS documents verified by lib/entitlements.js. Features map to tier keys; unknown keys are refused. In v3.0.15 enforcement is off by default: gated routes answer, and the header shows the tier. A 72-hour grace period covers an expired licence, and a clock-rollback high-water mark refuses a host whose clock went backwards. The Lighthouse panel shows the licence plane state (contract 1.0.2; errors are RFC 9457 problem documents).
Exclude a transcript population
Commander scans ~/.claude/projects for transcripts. One runaway population can stall the scan. TRANSCRIPT_EXCLUDED_DIRS names directories to skip; the flood alert (threshold and window are configurable) warns before that point. Archive sweeps move cold transcripts to an archive directory; the trash sweep purges deleted sessions after their retention.
Reference
Ports and processes
| Item | Default | Source |
|---|---|---|
| HTTP | 3200 (prod), 3210 (dev) | COMMANDER_PORT |
| WebSocket | 3201 (prod), 3211 (dev) | COMMANDER_WS_PORT |
| Bind | all interfaces | COMMANDER_BIND, COMMANDER_WS_BIND |
| Process | pm2 commander / commander-dev | ecosystem.config.js |
| Database | commander schema | DATABASE_URL |
Authentication endpoints
| Route | Auth | Purpose |
|---|---|---|
POST /api/auth | rate-limited | password login, sets cookie |
GET /api/auth/oidc/start, /callback | rate-limited | OIDC code flow |
POST /auth/sso/exchange | broker code | single-use broker exchange |
POST /api/auth/logout | session | revoke current session |
PATCH /api/settings with app_password | session + step-up | rotate the password; the server then revokes every other session |
GET /api/auth/check | none | auth mode, SSO enabled, whether the password form applies |
Settings families
| Family | Examples | Step-up |
|---|---|---|
| Login seam | app_password, auth_mode, auth_oidc_*, mcp_auth_base_url | yes |
| Terminal | term_default_cols, term_max_rows, term_block_suspend | no |
| Sessions | session_park_enabled, session_close_default, archive park minutes | no |
| UI | ui_native_context_menu, header_usage_meters, docs_ui | no |
| Deploy | promote_allow_override, ladder JSON per project | no |
| Vault | vault_password_length, vault_reveal_autohide_ms | no |
The full key list (204 settings keys, 63 client-config keys, 29 live tunables and the environment inventory) is in the reference.
Where data lives
| Data | Location | Encrypted |
|---|---|---|
| Sessions, projects, personas, prompts | PostgreSQL commander.* | no |
| Vault secrets, integration credentials, account keys | PostgreSQL, secret columns | AES-256-GCM |
| Transcripts | ~/.claude/projects on the host, indexed in DB | no |
| Screenshots, uploads, artifacts | configured directories, content-addressed for artifacts | no |
| Session tokens | DB as SHA-256 hash only | one-way |
Explanation
Why one configuration chain. A tunable that can be set in three places with no order is three bugs waiting. Commander's rule is that a literal is only allowed as the named default at the top of the chain, the settings table can override it, and the browser hardcodes nothing because it renders from /api/client-config. That is what lets the same build serve a personal host and a multi-tenant container.
Why the login seam is guarded twice. A session cookie proves someone logged in once. Changing how anyone logs in deserves fresh proof, so the step-up guard asks for the current password on those keys, and the runbook keeps the change on an operator's hand rather than an agent's.
Why secrets live outside the tree. A worktree is copied, promoted and sometimes committed. A 0600 file outside it cannot leak through any of those paths, and PTY_ENV_BLOCKLIST keeps the same values out of the environment that agent terminals inherit.
Why the vault exists. During one lockout an agent reset the production password and nobody could find it afterwards. The vault is the answer: one library, one table, one CLI, and a rule that every generated credential lands there in the same step.
Related
- DevOps for the promotion ladder, pm2, Guardian and Docker.
- Developer for how a setting or migration is added.
- Application for personas and MCP bindings.
- Reference for every key and route.
- 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 →