warden

Environment variables

Variables warden reads, and the ones warden run sets.

Every environment variable warden reads, and the ones warden run, batch and e2e set for your commands; look here when a setting needs to apply to a whole shell or CI job instead of one flag.

Read by warden

VariableEffect
WARDEN_HOMEstate directory (default ~/.warden): warden.db, builds, batches, ccache, affected, logs, skill, ui, simd, android, perf, tools
WARDEN_SESSION_ID, CLAUDE_CODE_SESSION_ID, CLAUDE_SESSION_IDtreat the caller as this agent session (first one set wins; a CI owner outranks them)
CI (true / 1), GITHUB_ACTIONS=truedetected as a CI owner; run id from GITHUB_RUN_ID, CI_PIPELINE_ID or BUILDKITE_BUILD_ID. CI also flips the e2e shutdown default to always
WARDEN_TRACE_PROCESSORpath to a trace_processor_shell for warden perf Android native analysis, instead of the pinned copy in $WARDEN_HOME/tools/ (profile an app)
WARDEN_GOLDEN=0don't clone new sims from golden images
WARDEN_ANDROID_SNAPSHOT=0Android: cold-boot claimed emulators instead of launching them from warden's quick-boot snapshot of the AVD, and never build one; =1 turns it on over a config android.snapshot: false. Only 0/1/true/false/on/off/yes/no count; another value is warned about and the config decides (Golden images)
WARDEN_EMULATOR_AUDIO=1Android: keep the emulator's audio (claimed emulators run -no-audio otherwise)
WARDEN_ARRANGE=0don't tile Simulator.app windows after iOS claims
WARDEN_HEADLESS=1 (or true)claim, run, batch, e2e: close the claimed sims' Simulator.app windows when no flag or config headless decides (Headless e2e); warden dev ignores it
WARDEN_PRIORITYboot queue priority (interactive|high|normal|low|N) when no --priority is given (Capacity & queue)
WARDEN_CAPACITY=offskip the capacity gate and boot queue
WARDEN_CAPACITY_MAXhard cap on weighted cpu units booted machine-wide (1 per iOS sim, 2 per Android emulator)
WARDEN_CAPACITY_HEADROOM_GBRAM held back from the budget (default max(8, 25% of RAM))
WARDEN_AUTO_GC=0turn off background auto-gc
WARDEN_MAX_CACHE_SIZEbuild cache cap (e.g. 50G, default 20G) for warden gc (--max-cache-size), warden builds prune (--max-size) and auto-gc, which runs when the cache is over it
ANDROID_HOME, ANDROID_SDK_ROOTAndroid SDK location (adb, emulator); without them warden falls back to PATH and cannot build the Android snapshot
NO_COLOR, FORCE_COLORoutput colour (a non-empty NO_COLOR turns it off)

| WARDEN_ANDROID_UIAD=0 | Android: don't use the resident UiAutomation server (warden-uiad); snapshots, settle and input go through uiautomator dump and input (Android driver) | | WARDEN_ANDROID_SETTEXT=1 | Android: fill over a field that already holds text replaces it with one warden-uiad setText call instead of select-all and typing (specs, warden ui fill); off by default until the server re-reads focus before setting (Android driver) | | WARDEN_BACKGROUND=0 | turn off every detached worker (auto-gc, SessionEnd shutdown) | | WARDEN_RELEASE_REPO | GitHub repo warden update downloads releases from (default delacournz/warden) | | WARDEN_SIMD, WARDEN_HELPER_DIR, WARDEN_SIMD_SOCKET, WARDEN_HELPER_URL | the iOS driver helper: binary, directory, running socket, download URL (iOS driver helper) | | WARDEN_SIMD_GUEST_MAX_AGE_S | read by the warden-simd helper: seconds after which it replaces its iOS accessibility guests before they wedge (default 12; 0 never; a non-positive or non-numeric value keeps the default) | | CODEX_HOME | Codex config directory (default ~/.codex) | | WARDEN_BINARY | npm package only: run this binary instead of the platform package's | | WARDEN_NPM_REGISTRY | npm registry warden update uses for npm installs (default https://registry.npmjs.org) |

Set by warden run

VariableValue
WARDEN_UDIDSevery claimed udid / serial
WARDEN_UDID_<i>the i-th device
WARDEN_LEASE_IDSevery lease id
WARDEN_PORTSevery leased port
WARDEN_PORT_<i>the i-th --port lease
WARDEN_APP_PATH, WARDEN_APP_HASHwith --app: installed build + fingerprint
WARDEN_HEADLESS1 when the run is headless (also for batch / e2e serve, setup and jobs, and dev's Metro); removed otherwise

Set for Metro

Every Metro warden starts (warden dev, warden e2e / batch serve) also gets these, so app.config.ts can tell worktrees apart. A native build never sees them, so the binary and its fingerprint stay the same.

VariableValue
WARDEN_WORKTREEthe checkout's folder name (basename of the git toplevel)
WARDEN_BRANCHthe current branch; absent on a detached HEAD

Name the dev server after the worktree in expo-dev-launcher's server list and "Recently opened":

// app.config.ts
const worktree = process.env.WARDEN_WORKTREE;
export default { name: worktree ? `My App · ${worktree}` : "My App" /* … */ };

Set for batch jobs

warden batch and warden e2e add these to every job, on top of the warden run variables above (details in e2e scripts):

VariableValue
WARDEN_UDIDthis job's device
WARDEN_WORKER, WARDEN_JOB, WARDEN_JOB_SEQ, WARDEN_PASS, WARDEN_BATCH_DIR, WARDEN_PORTworker index, job, run count on the worker, consecutive-pass index, batch directory, first leased port
WARDEN_LEGwith devices legs: the leg's profile name; WARDEN_UDIDS / WARDEN_UDID_<i> then hold only that leg's devices
WARDEN_METRO_URLe2e suites with a metro section: the run's Metro, also set for serve and setup
WARDEN_LOG_TIMESTAMPS1: warden stamps every log line with its arrival time, so the job prints no clock of its own

Set for spec processes

warden e2e starts each run of *.e2e.ts specs as bun test with these set. Setting WARDEN_UDID yourself runs a spec by hand (Write e2e specs); without it the @delacour/warden-e2e preload does nothing.

VariableValue
WARDEN_UDIDthe device (iOS udid or Android serial). Required for a by-hand run
WARDEN_DEVICE_NAMEthe leased device's name; pins devtools to its own Metro inspector page when several devices share a Metro
WARDEN_PLATFORMios (default) or android
WARDEN_BUNDLE_IDthe app under test: spec.bundleId, else the project's. Required for a by-hand run
WARDEN_APP_PATHthe installed build, for app.reinstall()
WARDEN_VERSIONthe warden version that started the run. The @delacour/warden-e2e preload exits 2 with the bun add -d @delacour/warden-e2e@<version> fix when its own (published) version differs; unset in a by-hand run, nothing is compared
WARDEN_METRO_URLwarden's Metro, for a dev client
WARDEN_LAUNCH_ARGSJSON array of the launch arguments app.open() passes: --initialUrl <metro url>, metro.launchArgs, spec.launchArgs
WARDEN_E2E_OUTwhere results.jsonl and the per-test artifacts go: <batchDir>/e2e/<worker>-<seq>
WARDEN_BINJSON array (or a bare path) of the warden that started the run; device.perf shells out to it. Set by warden e2e; without it device.perf throws SESSION ("WARDEN_BIN is not set")
WARDEN_PERFcomma list of captures every attempt records (js, react, native, metrics, frames, logs, all), from warden e2e --perf[=…]; empty means the default set (metrics, frames, logs). Unset: no automatic profiling
WARDEN_PERF_BUDGETSJSON of the suite's perf.budgets (startupColdMs, jankPct, maxCommitMs, p95CpuPct, peakMemoryMb); a breach fails the test with PERF_BUDGET. Set by warden e2e when the config has budgets
WARDEN_E2E_TIMEOUT, WARDEN_E2E_ACTION_TIMEOUT, WARDEN_E2E_ASSERTION_TIMEOUTthe spec timeouts in milliseconds (defaults 120000, 30000, 5000)
WARDEN_E2E_SPECS_DIRthe suite's specsDir, used to name each spec's spec id
WARDEN_SIMD_SOCKETthe pre-warmed iOS helper's socket (warmDrivers); unset, the driver starts a helper itself
WARDEN_SIMD, WARDEN_HELPER_DIRwhere the warden-simd binary is (iOS driver helper); warden e2e sets WARDEN_HELPER_DIR
WARDEN_SIMD_PACE_MS, WARDEN_SIMD_HOLD_MS, WARDEN_SIMD_REPEAT_GAP_MSread by the helper process: default keyboard pacing in ms (quiet after each key 30, key down to up 40, extra pause before a repeated key 300). A request's paceMs / holdMs / repeatGapMs wins. A React Native text field digests about 45-50 ms per key, so lower values lose characters
WARDEN_SIMD_IDLE_TIMEOUThow long a warden-simd that warden starts keeps running with no client and no request: a duration, 10m by default (iOS driver helper)
WARDEN_SIMD_TIMEOUThow long one request to a warden-simd that warden ui connects to may take before the client reports E_TIMEOUT: a duration, the client's 10 s by default. The first snapshot of a cold simulator on a hosted worker can need more (iOS driver helper)
WARDEN_SIMD_STREAM_IDLE_Sread by the helper process: close the quiescence stream after this many seconds without a settle. Unset or 0 keeps it open for the helper's life (iOS driver helper)
WARDEN_SIMD_TRACE1: the helper logs one debug event per quiescence sample (same as warden-simd --trace). Set it yourself
WARDEN_HEADLESS1 on a headless run
WARDEN_E2E_DEBUG1: log every helper operation and simctl call with its duration to stderr. Set it yourself
WARDEN_BUNread by warden e2e: the bun to run specs with (default: bun on PATH)

Read by warden ui record

Set these in the shell that runs warden ui fill --secure (and record) so the recorded spec holds a variable name, never the text (Author specs by recording).

VariableValue
WARDEN_UI_SECRET_<NAME>a secret value. A fill --secure (or any typed text equal to a non-empty value) is recorded as process.env.WARDEN_UI_SECRET_<NAME>, guarded to throw when unset. Set it for the spec run too (suite env). With none set, a secure fill uses WARDEN_UI_SECRET_SECURE_TEXT and a note to set it or edit the step

On this page