Skip to main content

Environment variables reference

ContextRelay is configured almost entirely through .contextrelay/config.json and the CLI. Environment variables exist as a thin override layer on top of that: they let you point a single invocation at a different port group, force activation on or off, tune a timeout, or arm one of the explicit autonomy gates.

Most users set none of these

The CLI exports the right project instance values automatically when you run ctxrelay claude, ctxrelay codex, or ctxrelay pair. You only reach for an environment variable when you need to override the automatic behavior - for example, to run a second ContextRelay on a non-default port group, or to force a session active or dormant for a one-off command.

The tables below mirror the canonical list. Each variable name, default, and behavior is verified against the source. They are split into two groups: a small Core set most people ever touch, and an Advanced tuning set of timing, autonomy, and install knobs that exist mainly for diagnostics and CI. The same split is reflected in the README (core only) and the full tuning table.

Core variables

These are the handful of variables you might actually set: activation, the port group, and runtime state. Everything else is in Advanced tuning below.

Activation: the variables most users touch

These control whether ContextRelay is active for a session and how Codex messages reach Claude. They sit on top of .contextrelay/config.json and the global activation flag - see Activation: auto-connect vs dormant for the full picture.

VariableDefaultDescription
CONTEXTRELAY_AUTO_CONNECTunsetHighest-precedence activation override. 1/true/yes/on forces ContextRelay active for the session; 0/false/no/off forces it dormant. Overrides the attach marker and both the project and global activation.autoConnect flags.
CONTEXTRELAY_MODEpush via ctxrelay claude, auto otherwiseMessage delivery mode: push, pull, or auto. push lets the daemon deliver Codex messages into Claude's context live; pull makes Claude drain them with get_messages / wait_for_messages.
CONTEXTRELAY_ALLOW_NAMED_SESSIONSunsetSet 1 to enable opt-in named runtime launch (ctxrelay claude --session <id>, ctxrelay codex --session <id>) and non-default live routing. Named runtime sessions are experimental and stay off until this is set.
Activation precedence in one line

The resolver checks sources in this order and stops at the first match: CONTEXTRELAY_AUTO_CONNECT → per-session attach marker → project activation.autoConnect → global activation.autoConnect → shipped default on. The environment variable always wins, which makes it the right tool for a single forced-active or forced-dormant command without changing any saved state.

Ports, state, and identity

ContextRelay assigns each project a stable instance ID and a three-port group automatically. You normally never set these; override them only to run multiple ContextRelay projects side by side, or to relocate runtime state.

VariableDefaultDescription
CODEX_WS_PORTauto group start 4500Codex app-server WebSocket port.
CODEX_PROXY_PORTauto group start 4501ContextRelay proxy port for the Codex TUI.
CONTEXTRELAY_CONTROL_PORTauto group start 4502Control port between the plugin/frontend and the daemon.
CONTEXTRELAY_PORT_BASE4500Starting point for automatic project port allocation.
CONTEXTRELAY_STATE_DIR.contextrelay/state during CLI launchesRuntime state directory.
CONTEXTRELAY_REGISTRY_DIRplatform app-state directoryGlobal instance registry directory.
XDG_STATE_HOME~/.local/state on LinuxLinux base directory for the global registry and direct/internal defaults.
CONTEXTRELAY_INSTANCE_IDgeneratedStable project instance id exported by the CLI.
CONTEXTRELAY_PROJECT_ROOTcurrent project rootProject root exported by the CLI.
CONTEXTRELAY_RUNTIME_SESSION_IDunsetRuntime session attached by ctxrelay claude --session <id>.
The port rule is all-or-none

Set all three of CODEX_WS_PORT, CODEX_PROXY_PORT, and CONTEXTRELAY_CONTROL_PORT, or set none of them. A partial override is rejected with an explicit error rather than silently guessing the missing ports. This guards against half-configured port groups that would route Claude and Codex to mismatched daemons.

# Correct: a full, self-consistent port group for a second project
CODEX_WS_PORT=4600 \
CODEX_PROXY_PORT=4601 \
CONTEXTRELAY_CONTROL_PORT=4602 \
ctxrelay pair

# Easier alternative - let the CLI pick a free group from a new base
ctxrelay pair --port-base 4600

Advanced tuning

Everything below is opt-in tuning. The shipped defaults are the supported configuration; reach for these only to debug a specific behavior, run in CI, or arm one of the explicit autonomy gates. None of these belong in the README, and none of them turn autonomy on by itself.

Message and turn coordination

These tune how Codex narration and turn lifecycle pings reach Claude. The shipped defaults implement the quiet, digest-based experience; override them to make the live channel more (or less) verbose.

VariableDefaultDescription
CONTEXTRELAY_TURN_DIGEST1Set 0 to disable turn-scoped digest buffering for Codex transcript messages.
CONTEXTRELAY_DIGEST_UNTAGGED1Set 0 to keep untagged Codex narration pushed live instead of buffering it into the turn digest.
CONTEXTRELAY_RESPECT_TARGET0Set 1 to suppress live Claude delivery for Codex messages whose explicit target is not claude; the ledger audit trail remains intact.
CONTEXTRELAY_QUIET_TURN_PINGS1Set 0 to push routine ⏳/✅ Codex turn-lifecycle pings into Claude's live context.
CONTEXTRELAY_FILTER_MODEfilteredMessage-routing filter mode. Set full for full routing payloads.
CONTEXTRELAY_MAX_BUFFERED_MESSAGES100Maximum undrained Claude-bound Codex messages per chat.
CONTEXTRELAY_MCP_TOOLSunsetOptional comma/space-separated MCP tool allowlist.
CONTEXTRELAY_ATTENTION_WINDOW_MS15000Coordination window used while waiting for a Codex turn to settle.
CONTEXTRELAY_PAIR_BINcontextrelayCLI binary used by ctxrelay pair.
CONTEXTRELAY_CLAUDE_DEVELOPMENT_CHANNELS1Set 0 to use Claude's approved channel path.
CONTEXTRELAY_NAMED_CODEX_RUNTIME_START_ATTEMPTS3Maximum daemon-side launch attempts for named Codex runtime ports.

Timeouts and reliability bounds

ContextRelay applies several wall-clock budgets so a stuck agent, a dropped TUI, or a hung turn cannot wedge a session. These are the knobs you adjust when a long legitimate turn keeps getting force-cleared.

VariableDefaultDescription
CONTEXTRELAY_TURN_MAX_MS300000Wall-clock budget for one Codex turn. Set 0 to disable.
CONTEXTRELAY_CODEX_TURN_IDLE_TIMEOUT_MS300000Codex silence window before a stuck turn is force-cleared.
CONTEXTRELAY_CLAUDE_RESPONSE_TIMEOUT_MS300000Timeout before a Claude-owned active task lane is considered stale.
CONTEXTRELAY_CLAUDE_PROBE_TIMEOUT_MS3000Liveness probe timeout before stale Claude eviction. Set 0 to disable.
CONTEXTRELAY_CLAUDE_STALE_REAP_MS90000Minimum Claude attachment age before status broadcasts may reap it.
CONTEXTRELAY_CLAUDE_STALE_REAP_COOLDOWN_MS60000Cooldown between stale Claude attachment probes.
CONTEXTRELAY_IDLE_SHUTDOWN_MS30000Idle daemon shutdown window.
TUI_DISCONNECT_GRACE_MS2500Grace after a Codex TUI disconnect before treating it as gone.
CONTEXTRELAY_MAX_DEPTH3Maximum relay recursion depth.
CONTEXTRELAY_DAEMON_SHUTDOWN_STEP_TIMEOUT_MS4000Per-step daemon shutdown cleanup deadline.
CONTEXTRELAY_DISABLED_RECOVERY_INTERVAL_MS5000Poll interval for the bridge's dormant-state recovery loop while ContextRelay is disabled for the session.
CONTEXTRELAY_MAX_CONTROL_MESSAGE_BYTES1000000Maximum accepted control WebSocket message size.
CONTEXTRELAY_MAX_CONTROL_MESSAGES_PER_MINUTE120Per-control-connection rate limit.
"Daemon disconnected" during a long turn

A "daemon disconnected" message in the middle of a genuinely long turn is often the CONTEXTRELAY_TURN_MAX_MS watchdog firing at 5 minutes, not a real crash. Check ctxrelay status before restarting anything. If you routinely run long turns, raise the budget (for example CONTEXTRELAY_TURN_MAX_MS=900000) rather than fighting the reset. See Troubleshooting.

Autonomy, idle scanner, and backup agents

Read-only by default

The idle scanner, read-only backup agents, and the idle worker fleet are opt-in. The variables below tune or hard-disable those features; none of them turns autonomy on by itself - the autonomy.* config and the ctxrelay autonomy / ctxrelay idle-scanner commands gate the actual behavior. See Autonomy and safe automation.

VariableDefaultDescription
CONTEXTRELAY_IDLE_SCANNER1Set 0 to hard-disable the idle-opportunity scanner scheduler regardless of the configured mode.
CONTEXTRELAY_IDLE_ACTION_TIMEOUT_MS120000Timeout for one idle-scanner act read-only worker.
CONTEXTRELAY_IDLE_ACTION_TOKEN_BUDGET120000Env override for autonomy.idleActionBudgets.tokenBudget, the per-daemon-session token budget for idle-scanner act workers.
CONTEXTRELAY_IDLE_ACTION_COST_BUDGET_USD1Env override for autonomy.idleActionBudgets.costBudgetUsd, the per-daemon-session cost budget in USD for idle-scanner act workers.
CONTEXTRELAY_IDLE_ACTION_BUDGET_WARNING_PCT80Env override for autonomy.idleActionBudgets.warningPct, the budget-pressure percentage that surfaces warnings.
CONTEXTRELAY_IDLE_ACTION_COMPARISON_EXTRA_BUDGET_USD0Env override for autonomy.idleActionBudgets.comparisonExtraBudgetUsd, extra per-session USD headroom for manual idle-evaluation compare estimates before the nightly cap is checked.
CONTEXTRELAY_IDLE_FLEET_MAX_WORKERS3Maximum read-only leaf workers for one high-confidence act fleet.
CONTEXTRELAY_IDLE_EVAL_ENABLEDunsetRequired explicit opt-in for real-token idle-evaluation probes; unset keeps probe dispatch fail-closed.
CONTEXTRELAY_IDLE_EVAL_NIGHTLY_CAP_USDunsetRequired nightly USD cap for real-token idle-evaluation probes, enforced above per-session budgets when evaluation is enabled.
CONTEXTRELAY_BACKUP_THROTTLE_MS60000Minimum delay between backup starts for the same target.
CONTEXTRELAY_BACKUP_KILL_GRACE_MS2000Grace between a backup-timeout SIGTERM and SIGKILL.
CONTEXTRELAY_WORKERunsetInternal worker-mode signal for read-only act and backup workers. Set to 1 by the daemon when it spawns a worker process; it prevents the worker from recursively starting another ContextRelay. Do not set this yourself.

act:write (autonomous edits)

Enabling act:write - letting an idle worker make contained file edits - is off by default and stays fail-closed behind several gates. Two of those gates are environment variables that no config value can supply, which is what makes the kill-switch real:

VariableDefaultDescription
CONTEXTRELAY_WRITE_MODE_ENABLEDunsetHard env kill-switch for act:write. Must be 1/true/yes/on before any contained writable worker can run; no config can supply it, so a config alone never enables writes.
CONTEXTRELAY_WRITE_DAILY_CAP_USDunsetRequired per-day USD cap for act:write. A missing or invalid cap fails the contained writable worker closed.
CONTEXTRELAY_IDLE_WRITE_TIMEOUT_MS180000Wall-clock timeout for one contained act:write worker before it is force-killed.
These two are necessary, not sufficient

Setting both variables does not turn writing on. act:write also requires autonomy enabled, write-mode set to act, positive budgets, owner/kind allowlists, the strict dual-idle gate, and worktree containment - every gate must pass. Even then, a contained worker edits only inside an ephemeral git worktree and never commits, merges, or pushes. See Enabling act:write safely.

Install, hooks, and host integration

These cover the npm install/uninstall lifecycle, the bundled Claude Code hook scripts, and a few internal overrides. They are mostly relevant to CI, packaging, and local development.

VariableDefaultDescription
CONTEXTRELAY_AUTO_REGISTERunsetOpt into automatic Claude plugin registration during npm install.
CONTEXTRELAY_AUTO_UNREGISTERunsetOpt into removing Claude plugin registration during uninstall.
CONTEXTRELAY_POSTINSTALL_DRY_RUNunsetPrint postinstall/preuninstall actions without changing registration.
CONTEXTRELAY_POSTINSTALL_STRICTunsetMake optional postinstall registration failures exit non-zero.
CONTEXTRELAY_DAEMON_ENTRYbundled daemonPlugin daemon entry override.
CONTEXTRELAY_ALLOW_DAEMON_ENTRY_OVERRIDEunsetSet 1 only for local development/tests.
CONTEXTRELAY_HEALTH_HOOK_COOLDOWN_SECONDS120Cooldown for bundled Claude health-check hook reminders.
CONTEXTRELAY_HOOK_STATE_DIR${TMPDIR:-/tmp}/contextrelay-hooksState directory for bundled hook scripts.
CONTEXTRELAY_HOOK_COMPACTunsetSet 1 to force compact UserPromptSubmit hook output.
CONTEXTRELAY_HOOK_PREVIEW_LIMITmode presetOverride the pending-message preview count.
CONTEXTRELAY_HOOK_PREVIEW_CHARSmode presetOverride the pending-message preview character cap.
CONTEXTRELAY_HOOK_DEDUPE_SECONDSmode presetOverride the identical-hook-output suppression window.
CONTEXTRELAY_CODEX_PROXY_TOKENgeneratedInternal Codex remote-auth token.
CONTEXTRELAY_CODEX_APP_SERVER_TRANSPORTautoInternal Codex app-server transport override.
CONTEXTRELAY_LOG_MAX_BYTES52428800Maximum size of a ContextRelay process log file before it is truncated. 0 disables the cap.

Claude Code host variables

These are supplied by Claude Code itself to the bundled hooks; ContextRelay reads them but does not own them.

VariablePurpose
CLAUDE_PLUGIN_ROOTBundled hook-script root supplied by Claude Code.
CLAUDE_PROJECT_DIRClaude project directory when supplied by Claude Code. ContextRelay falls back to PWD.

The partial-override rule, restated

Because it is the one rule that bites people, it is worth repeating on its own:

Environment overrides that come in groups must be set as a complete group. The clearest example is the port trio - set all of CODEX_WS_PORT, CODEX_PROXY_PORT, and CONTEXTRELAY_CONTROL_PORT, or none. A partial set is rejected, not patched.

If you only need a different port group, prefer ctxrelay pair --port-base <port> (or ctxrelay claude / ctxrelay codex with the same flag) and let the CLI export a consistent group for you.

Next steps