Documentation menu

Environment variables

Three separate sets of variables matter, and conflating them is a security bug:

  1. What locac reads: your keys, your home directory, your shell.
  2. What locac sets for the commands it spawns: the run's identity, and nothing more.
  3. What a spawned command inherits: a short allow-list, because everything locac spawns is running target code.

What locac reads

Provider credentials

Every built-in provider reads one key variable and one base-URL override. The full table is on the Providers page; the key variables are:

ANTHROPIC_API_KEY · OPENAI_API_KEY · GROQ_API_KEY · OPENROUTER_API_KEY · TOGETHER_API_KEY · XAI_API_KEY · DEEPSEEK_API_KEY · MOONSHOT_API_KEY · ZAI_API_KEY · VLLM_API_KEY · LMSTUDIO_API_KEY · OLLAMA_API_KEY

DEEPSEEK_API_KEY and MOONSHOT_API_KEY each serve two providers: that vendor's Anthropic-shaped and OpenAI-shaped endpoints both read the same variable.

The matching base-URL overrides are ANTHROPIC_BASE_URL, OPENAI_BASE_URL, GROQ_BASE_URL, OPENROUTER_BASE_URL, TOGETHER_BASE_URL, XAI_BASE_URL, ZAI_BASE_URL, DEEPSEEK_BASE_URL, DEEPSEEK_OPENAI_BASE_URL, MOONSHOT_BASE_URL, MOONSHOT_OPENAI_BASE_URL, MOONSHOT_OPENAI_CN_BASE_URL, VLLM_BASE_URL, LMSTUDIO_BASE_URL, OLLAMA_OPENAI_BASE_URL, and OLLAMA_HOST for the native Ollama protocol.

A key can also be referenced from the config file rather than inlined, which keeps the secret in your shell profile and out of a file you might paste:

{ "apiKey": "$ANTHROPIC_API_KEY" }

Both "$VAR" and "${VAR}" are expanded once, at run time. An unset variable resolves to nothing rather than to an empty string, so the run fails loudly instead of sending a blank credential.

locac's own

VariableDefaultWhat it does
LOCAC_HOME~/.locacWhere config, skills, roles, keybindings, session databases and extracted runtime assets live. Never the target repository
LOCAC_SHELLauto-discoveredPath to a bash-compatible shell for bash, execute and the sandbox. Same as --shell-path
LOCAC_PASSTHROUGH_ENVunsetComma-separated extra variable names to admit into spawned commands. See the allow-list below

LOCAC_HOME is the anchor of the whole trust model. Everything locac treats as operator input, from configuration and prompt overrides to skills, roles and key bindings, is read from there and nowhere else. The repository under audit is attacker-authored, so a config read from it would let the target choose your provider, your base URL and your API key.

Shell resolution

LOCAC_SHELL sits second in a five-step chain:

  1. --shell-path, or shellPath from the config (CLI and per-project overrides already merged)
  2. LOCAC_SHELL
  3. bash on PATH, unless it is the WSL launcher, which is skipped in favour of a native bash
  4. Common Windows install locations
  5. No bash: fall back to sh, else PowerShell (-NoProfile -NonInteractive), else cmd /c

An explicit-but-missing path does not hard-fail. It warns once and falls through to auto-discovery, so a stale config entry can never brick a run:

locac: shellPath "C:\Program Files\Git\bin\bash.exe" not found; falling back to auto-discovery

The PowerShell fallback is deliberately not given -ExecutionPolicy Bypass: the target repo is attacker-authored, and its .ps1 files should stay exactly as hard to run as your host says they are.

Editor

VariableRead byOrder
EDITOR, VISUALlocac skills edit <name>$EDITOR, then $VISUAL
VISUAL, EDITORThe TUI's Ctrl+G draft handoff$VISUAL, then $EDITOR

The two orders differ on purpose. Ctrl+G hands your whole draft to an editor and takes back what you save, which wants the POSIX definition of VISUAL, the full-screen editor. skills edit just opens a file.

With neither set, skills edit prints the path instead of failing:

/home/you/.locac/skills/my-skill/SKILL.md
(set $EDITOR to open it automatically)

A value with arguments works: EDITOR="code -w" reaches the spawn as two words.

Terminal

VariableEffect
NO_COLORSet to anything, and all ANSI colour is suppressed
TERM=dumbSame

Colour is emitted only when stdout is a real TTY and neither of those applies, so piping to a file or a pager already gives you plain text without setting anything.

What locac sets for spawned commands

Every command locac spawns, whether the bash tool, execute, a sandbox-wrapped argv or a target build, inherits these:

VariableValue
LOCAC_SESSION_IDThe current session id. Absent when there is no session
LOCAC_MODELThe active model id
LOCAC_PROVIDERThe active provider id
LOCAC_EFFORTThe reasoning level actually going out on the wire. Absent, not empty, when the endpoint carries none

LOCAC_EFFORT is omitted rather than set to "" because a script asking [ -n "$LOCAC_EFFORT" ] should be able to tell "the operator chose no effort" from "this endpoint has no effort knob".

These let a PoC stamp its output with the run it belongs to, and let a build detect that it is running under the harness, without a side-channel file.

What is deliberately not there is the design:

  • Never the API key. One env in a Makefile or a postinstall script would exfiltrate it.
  • Never the session database path. The store lives outside the sandbox root, so naming it to attacker-authored code buys the attacker a target and locac nothing.

What a spawned command inherits

Handing target code {...process.env} hands it your provider keys, your cloud and CI tokens, and LOCAC_HOME, which points at the file holding the key. So locac allow-lists its own environment down to what a process needs in order to start, and drops everything else.

Matching is case-insensitive, because Windows spells these Path, SystemRoot, ComSpec, and a case-sensitive list would strip PATH itself there.

POSIX: PATH · HOME · SHELL · TMPDIR · USER · LOGNAME · LANG · LC_ALL · LC_CTYPE · TERM · TZ

Windows (a process will not even start without the first three): SYSTEMROOT · SYSTEMDRIVE · WINDIR · COMSPEC · PATHEXT · TEMP · TMP · USERPROFILE · HOMEDRIVE · HOMEPATH · APPDATA · LOCALAPPDATA · PROGRAMDATA · PROGRAMFILES · PROGRAMFILES(X86) · PROGRAMW6432 · COMMONPROGRAMFILES · COMMONPROGRAMFILES(X86) · COMMONPROGRAMW6432 · ALLUSERSPROFILE · PUBLIC · DRIVERDATA · NUMBER_OF_PROCESSORS · PROCESSOR_ARCHITECTURE · OS

Note what is absent: no ANTHROPIC_API_KEY, no GITHUB_TOKEN, no AWS_*, no LOCAC_HOME.

Re-admitting a variable

Real builds need real variables. LOCAC_PASSTHROUGH_ENV is the operator's escape hatch, never the target's, because the target cannot set it:

export LOCAC_PASSTHROUGH_ENV="JAVA_HOME,MAVEN_OPTS,CARGO_HOME"
locac run "build the project and audit the deserialization path" --auto

Names are comma-separated, trimmed and matched case-insensitively.

Add only what the build actually needs. Every name you add is a name the target's Makefile can read.

Next

Configuration covers the config file these variables interact with, and the Flag reference lists their command-line equivalents: --shell-path for LOCAC_SHELL, --api-key and --base-url for the provider variables, and --config to point at a config file outside LOCAC_HOME entirely.