Runner environment variables
A runner installed with the one-line installer needs none of these: it is configured by pairing, and the service definition it writes carries no secrets. They are here for the cases where you run the program yourself.
Every variable on this page is read by the runner’s own process. None of them is handed to a coding agent, with one deliberate exception: with full setup on, CLAUDE_CONFIG_DIR and CODEX_HOME point runs at your own CLI folders.
Connection
Section titled “Connection”| Variable | Default | Does |
|---|---|---|
COMMANDCHAIN_URL | http://localhost:7000 | Base URL of the CommandChain instance. Used as the default for connect, start, and every creds subcommand |
COMMANDCHAIN_TENANT_ID | none | Workspace this runner serves. Used by start only. connect learns it from pairing |
COMMANDCHAIN_RUNNER_CREDENTIAL | none | Bootstrap credential used to register. Used by start only |
COMMANDCHAIN_RUNNER_NAME | none | Display name. Used by start only. connect defaults to <user>@<hostname> |
COMMANDCHAIN_RUNNER_CREDENTIALS_FILE | ~/.commandchain/runner.json | Where the pairing credential is stored. Defaults to runner.json inside COMMANDCHAIN_RUNNER_STATE_DIR; setting this variable overrides that. Intended for scripts and test harnesses that need a throwaway file |
COMMANDCHAIN_RUNNER_STATE_DIR | ~/.commandchain | The one directory for everything the runner keeps: the pairing credential (runner.json), workspaces (workspaces/), its own coding-agent sign-ins (agent-auth/), the agent setup choice (agent-setup.json) and its lock files. The installer passes it to the installed service. Every command on the machine that should see the same runner needs the same value |
Execution
Section titled “Execution”| Variable | Default | Does |
|---|---|---|
COMMANDCHAIN_WORKSPACE_ROOT | ~/.commandchain/workspaces | Root directory for repository checkouts. Defaults to workspaces/ inside COMMANDCHAIN_RUNNER_STATE_DIR; setting this variable overrides that. One long-lived directory per repository |
COMMANDCHAIN_POLL_INTERVAL_MS | 2000 | Heartbeat interval, in milliseconds |
COMMANDCHAIN_CALLBACK_PORT | 0 | Port for the local callback API. 0 picks a free one |
COMMANDCHAIN_TUNNEL_MAX_SESSIONS | 5 | Concurrent live-app tunnel sessions. A value that is not a positive integer is ignored with a warning |
TENANT_CONCURRENCY_CAP | 5 | Runs this process will execute at once for one workspace |
CHAT_CONCURRENCY | 3 | Concurrent huddle turns this process will handle at once |
LOG_LEVEL | info | debug when you are diagnosing something. The log is at ~/.local/state/commandchain-runner/ on Linux and ~/Library/Logs/commandchain-runner/ on macOS |
COMMANDCHAIN_RUNNER_PLAIN | none | Set to 1 to turn off the runner screen and write structured JSON logs instead, the same as --plain. Non-interactive terminals already get the JSON form without it |
Adapter paths
Section titled “Adapter paths”Each coding agent is found on PATH by default. Set these when the binary lives somewhere else.
| Variable | Default | Adapter |
|---|---|---|
COMMANDCHAIN_AGENT_PATH | commandchain-agent | The built-in agent (commandchain-agent-cli) |
CLAUDE_PATH | claude | Claude Code (claude-code-cli) |
CODEX_PATH | codex | Codex (openai-codex-cli) |
GEMINI_PATH | gemini | Gemini (gemini-cli) |
Adapter default models
Section titled “Adapter default models”Used only when the control plane has not resolved a model for the run. A model from the wrong provider family fails loudly rather than being quietly swapped.
| Variable | Default | Adapter |
|---|---|---|
COMMANDCHAIN_DEFAULT_MODEL | claude-sonnet-4-6 | The built-in agent |
CLAUDE_DEFAULT_MODEL | sonnet | Claude Code |
CODEX_DEFAULT_MODEL | gpt-5.4 | Codex |
GEMINI_DEFAULT_MODEL | the CLI’s own default | Gemini |
Callback token verification
Section titled “Callback token verification”There is no shared secret to configure, and no variable in this section.
While a run executes, the coding agent calls back into the runner with a short-lived attempt token the platform issued for that one attempt. The runner verifies that token with a key derived from its own pairing credential, and the platform signs it with the same key derived from the stored hash of that credential. Pairing already gave the runner the credential, so a runner installed with the one-liner has everything it needs to verify callbacks the moment pairing completes.
The platform’s own signing secret never leaves the platform. That is deliberate: a key that verifies attempt tokens for one runner must not be a key that mints tokens for every workspace, and a per-runner key cannot be.
A per-runner key is also not a key for every run. When the platform receives an attempt token, it checks the signature against the named runner’s key and then checks the attempt itself: the attempt has to have been opened for that runner and its run has to still be assigned to it, in the workspace the token names. A token that names someone else’s attempt, another workspace, or a capability the run was never granted is refused, whoever signed it.
Two consequences worth knowing:
- A runner that is not paired refuses to start, with a message that says so. It has no credential, so it could not verify anything the agent will hold, and starting anyway would mean claiming work and then rejecting every callback, which reads as a platform fault.
- Rotating a runner’s credential from the platform changes its key. A run already in progress keeps verifying its existing token until that token would have expired on its own, then the runner has to be restarted with the new credential. See pairing.
JWT_SECRET and DEV_JWT_SECRET are not read by the runner. Setting them has no effect.
Logins uploaded for managed runs
Section titled “Logins uploaded for managed runs”creds push, and the c key on the runner screen, read your own coding CLI logins where those tools keep them, to upload one for runs on CommandChain’s managed servers. These variables move where they look, or supply a key directly.
| Variable | Default | Read for |
|---|---|---|
CLAUDE_CONFIG_DIR | ~/.claude | Where Claude Code’s credential file is looked for. With full setup on, also the folder runs use |
CODEX_HOME | ~/.codex | Where Codex’s auth.json is looked for |
ANTHROPIC_API_KEY / CLAUDE_API_KEY | none | An Anthropic key, when there is no Claude Code login on the machine |
OPENAI_API_KEY | none | An OpenAI key, read from Codex’s auth.json or the environment |
GEMINI_API_KEY / GOOGLE_API_KEY | none | A Google key, when there is no Gemini login on the machine |
An API key is preferred over a subscription login when both are present, because a key is revocable and avoids the provider’s terms-of-service question.
What reaches a coding agent
Section titled “What reaches a coding agent”A coding agent does not inherit the runner’s environment. Each agent process is built from an allowlist, because a coding agent has a shell and network access, so anything in its environment is one request away from leaving the machine.
An agent child gets exactly three things:
- An execution baseline.
PATH,HOME,USER,LOGNAME,SHELL,TMPDIR,LANGandLC_*,TZ,TERM, and certificate-bundle paths. Paths and locale, no credentials. - The run’s selected environment. The project’s own values, released for that attempt only, injected into the process and never written into the workspace.
- That adapter’s own sign-in, and only its own. On a runner on your own machine, a copy of the runner’s own sign-in; on a managed run, the credential the platform released for that attempt. Either way it is staged into a synthetic home for that attempt. Provider keys in the runner’s own environment (
ANTHROPIC_API_KEYfor Claude,OPENAI_API_KEYfor Codex,GEMINI_API_KEYfor Gemini) are blanked in the child, so no adapter ever receives a key it was not signed in with and the run always executes as that sign-in.
Everything else is dropped: the runner’s own control-plane credential, its workspace identifiers, GITHUB_TOKEN, and any unrelated key you happen to have exported.
Next steps
Section titled “Next steps”- Runner CLI: the flags these variables back.
- The runner security model: the boundary these rules sit inside.
- Sign-in only or full setup: the runner’s own sign-in.
- Managed runner credentials: upload, storage, and one-time release for managed runs.