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_USAGE_MODEunsetPer-invocation usage-preset override (off, lean, strict); highest precedence, never persisted. Covers shared-context tool shaping, headless output caps, and the bridge contract reminder cadence. For CI/automation.
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.
CONTEXTRELAY_RATE_LIMIT_RESUME(config)Override the rate-limit auto-resume mode for this run: off, notify, or resume. See rateLimitResume in config.json.
CONTEXTRELAY_RATE_LIMIT_RESUME_POLL_MS30000Poll cadence while a captured rate-limited message waits for its reset time.
CONTEXTRELAY_RATE_LIMIT_RESUME_EXPIRY_MS172800000Hard TTL after which a persisted capture is discarded (48 h).
"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_DEFERRED_DISPATCH_TTL_MS900000Hard TTL for an act-mode dispatch deferred while awaiting strict quiescence.
CONTEXTRELAY_IDLE_DEFERRED_DISPATCH_POLL_MS20000Poll interval while an act-mode dispatch is deferred awaiting quiescence.
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. It has no environment variables of its own: you arm it entirely through config (autonomy.writableAction.enabled plus a positive autonomy.writableAction.budgetUsd). The contained worker always runs inside Codex's workspace-write OS sandbox, which confines writes to the worktree + system temp; your project and its .contextrelay/config.json live outside those, and act:write refuses if the project is under system temp - so the worker cannot re-arm itself. The only env knob is the worker's wall-clock timeout:

VariableDefaultDescription
CONTEXTRELAY_IDLE_WRITE_TIMEOUT_MS180000Wall-clock timeout for one contained act:write worker before it is force-killed.
Config arms it; the sandbox keeps it contained

Setting enabled: true and a positive budgetUsd does not let act:write escape its bounds. The contained worker still runs only when the strict dual-idle and single-flight gates pass and the daily budget check holds, and it always executes inside Codex's workspace-write OS sandbox on an ephemeral git worktree - it never commits, merges, or pushes. That sandbox confines writes to the worktree and the system temp area; your primary tree and config live outside those, and act:write refuses to run if the project is itself under a system-temp directory - so the worker cannot escalate or re-arm itself. (Live-verified on macOS; the Codex sandbox is expected to behave the same on other platforms but is not yet separately verified.) See Enabling act:write safely.

Headless reviewers

On-demand headless reviewers (contained_run / ctxrelay headless run) fire one read-only job through a registered contained adapter (codex, claude, and conditionally opencode) for a fresh-context second opinion. The daemon pools and budgets these jobs; headless.enabled defaults to true and is independent of the autonomy config — asking for a one-shot review is normal collaboration, not an autonomous action.

VariableDefaultDescription
CONTEXTRELAY_HEADLESS_ENABLED1Master kill switch for the whole headless surface. Set 0 to disable contained_run regardless of config.
CONTEXTRELAY_HEADLESS_MAX_CONCURRENCY4Maximum concurrent headless jobs the daemon pool will admit.
CONTEXTRELAY_HEADLESS_MAX_QUEUE64Maximum jobs queued waiting for a pool slot; excess requests are rejected.
CONTEXTRELAY_HEADLESS_BUDGET_USD0Optional per-session post-hoc USD spend cap (0 = uncapped).
CONTEXTRELAY_HEADLESS_TIMEOUT_MS180000Per-job wall-clock timeout before the headless worker is force-killed.
CONTEXTRELAY_OPENCODE_ENABLED1Opencode-adapter kill switch. Set 0/false/off to hide only the opencode headless adapter even when the CLI and macOS sandbox probe are available.
CONTEXTRELAY_OPENCODE_MODELunsetOptional model for the read-only opencode headless adapter. When unset (and OPENCODE_MODEL is also unset), the adapter passes no --model and lets opencode use its own default.
CONTEXTRELAY_OPENCODE_AUTH_JSONunsetOptional path to an opencode auth.json file copied into the adapter's isolated sandbox HOME for a single job. Provider API key env vars are not forwarded to opencode.
CONTEXTRELAY_OPENCODE_USE_STORED_AUTHunsetSet 1 to copy ${HOME}/.local/share/opencode/auth.json into the adapter's isolated sandbox HOME. Leave unset to require explicit CONTEXTRELAY_OPENCODE_AUTH_JSON or another OPENCODE_* auth mechanism.

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. Expanded by Claude Code from ${CLAUDE_PLUGIN_ROOT} in hooks.json/.mcp.json; not read from process.env by ContextRelay's own 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