Skip to main content

Environment Variables

Everything envy reads from or writes to the environment. Most projects set none of these.

Read by envy​

VariableEffect
ENVY_CACHE_ROOTCache root. Same as --cache-root, and the highest-priority tier. Read by the binary and by the committed bootstrap scripts.
ENVY_MIRRORWhere to download envy releases from. Overrides the @envy mirror directive. https:// and s3:// both work.
ENVY_IGNORE_DEPOTSet to skip depot lookups and build from source. Same as --ignore-depot, honored by sync, install, package, and export.
ENVY_NO_REEXECSet to stop envy from re-executing into the version the manifest pins. Debugging only.
ENVY_FETCH_BUDGET_MSHow long envy keeps retrying one transient download, measured from its first failure. Default 90000, clamped to 0 through 3600000. Requires envy 0.4.6. See retries.
ENVY_FETCH_ATTEMPTSCeiling on attempts for one download, counting the first try. Default 10 from envy 0.4.6, 3 before that. Clamped to 1 through 100. The budget is normally what stops a retry loop.
ENVY_FETCH_RETRY_BASE_MSBase backoff between those attempts, in milliseconds. Default 1000, clamped to 0 through 60000. 0 disables the wait.

All three fetch knobs are read once per process. They exist for CI and for tests that cannot afford real backoff.

Written by envy​

VariableSet byValue
ENVY_PROJECT_ROOTenvy run, the shell hook, and each deployed product scriptThe governing manifest's directory. A product script stamps it as a hop relative to its own bin directory, and only for a root manifest.
PATHenvy run, the shell hook, and each deployed product scriptThe project's bin directory, prepended.

envy run sets both for the child process only. The shell hook sets both in your interactive shell and undoes them when you leave the project. A product script sets them for the tool it execs, so a tool that shells out to a sibling product finds it.

Nothing else is injected. A phase's envy.run inherits the ambient environment, plus whatever you pass in opts.env.

Shell hook controls​

Read by the hook scripts, not by the binary. See Shell Hooks.

VariableEffect when set to 1
ENVY_SHELL_HOOK_DISABLEThe hook does nothing. Useful in scripts and in CI where the hook is installed but unwanted.
ENVY_SHELL_NO_ENTER_EXIT_ANNOUNCENo entering/leaving messages on stderr.
ENVY_SHELL_NO_ICONNo 🦝 prompt marker.

Variables the hook keeps for itself, such as _ENVY_BIN_DIR and _ENVY_ORIG_PS1, start with an underscore and are not exported. Do not set them.

Windows​

The variables are the same. Three details differ:

  • Set them the usual PowerShell way, $env:ENVY_CACHE_ROOT = "C:\envy-cache", or set ENVY_CACHE_ROOT=C:\envy-cache in cmd.
  • ENVY_CACHE_ROOT and ENVY_MIRROR are read by bin\envy.bat as well as by the binary, so they steer the bootstrap download too.
  • The hook variables apply to the PowerShell hook. cmd.exe has no hook, so nothing there reads them.

Cache root precedence​

Highest to lowest:

  1. --cache-root or ENVY_CACHE_ROOT. Must be absolute.
  2. A .envy-cache-local or .envy-cache-shared marker written by envy cache --local/--shared.
  3. @envy cache-mode in the manifest.
  4. @envy cache-local being present at all, which means local. Relative to the manifest's directory, with no expansion of any kind.
  5. The platform default.
PlatformDefault
macOS$HOME/Library/Caches/envy
Linux$XDG_CACHE_HOME/envy, or $HOME/.cache/envy
Windows%LOCALAPPDATA%\envy, or %USERPROFILE%\AppData\Local\envy

Every tier resolves to an absolute path. A relative manifest directive anchors to the manifest, not to your current directory, so one manifest always names one cache tree.

Mirror precedence​

  1. ENVY_MIRROR
  2. @envy mirror in the manifest
  3. GitHub releases

Download retries​

A download that dies on the transport is retried rather than failing the run: DNS, connect, or TLS failures, a connection that dies mid-body, a stall below the minimum transfer rate, and HTTP 5xx or 429. Every other 4xx is a statement about the request that a replay will not change, and a malformed URL or a local filesystem failure is fatal immediately.

Retrying is safe because everything envy fetches is an idempotent GET and every payload is verified against its sha256 after transport, so a replay cannot launder bad bytes.

From envy 0.4.6, what ends the retrying is a wall-clock budget rather than a bare attempt count. ENVY_FETCH_BUDGET_MS is 90 seconds by default, measured from the first failure, so an attempt that burns its own connect timeout spends the budget as surely as a wait does. ENVY_FETCH_ATTEMPTS survives as a ceiling. A connection that never completes a handshake is capped at 5 seconds instead: a host that will not answer is down rather than busy, and throttling is what recovers inside a minute.

Backoff is exponential and jittered: 1x, 2x, then 4x ENVY_FETCH_RETRY_BASE_MS, capped at 30 seconds per wait, each spread over plus or minus 50%. Doubling rather than quadrupling keeps several attempts inside the first few seconds, where transient faults live. envy runs a thread per request, so without the jitter a batch that all failed against one bad mirror would march back onto it in lockstep.

A server that sends Retry-After with its 429 or 503 sets a floor on the next wait, jittered upward but never back below what it asked for. A cooldown longer than the budget has left ends the fetch rather than coming back early to earn the same refusal. s3:// sources are not retried here, because the AWS SDK already retries internally.

Since a minute and a half of silence would read as a hang, a package waiting out a backoff says so on its own row, counting down:

[acme.tool@r1] retry 2 in 6s (http_status) tool.tar.gz

Each retry is a download_retry trace event, and shows up under --verbose as:

[DBG] [acme.tool@r1] fetch: attempt 2 failed (http_status), retrying in 2754ms: HTTP error 503 from https://example.com/tool.tar.gz

A transport failure that is not an HTTP status now carries the status and Content-Type that did arrive, which is what separates a real network fault from a server answering 200 text/html and closing the connection before the body:

Failure when receiving data from the peer after 0 of 54881 bytes (HTTP 200, text/html)

envy also sends a real User-Agent, envy/<version> plus its repository URL, because the placeholder it used before is the kind of thing abuse heuristics flag.

Variables envy respects indirectly​

VariableWhere it matters
HOME, XDG_CACHE_HOME, LOCALAPPDATA, USERPROFILEDefault cache root, and the ~ in .luarc.json paths.
TERMWhether output gets a live TUI or plain lines.
LANG, LC_ALL, LC_CTYPEWhether the shell hook uses UTF-8 glyphs or ASCII fallbacks.
AWS_*Ambient credentials for s3:// sources, mirrors, and depots. envy uses the AWS SDK directly.

CI​

Two settings cover most CI needs:

env:
ENVY_CACHE_ROOT: ${{ github.workspace }}/.envy-cache # so the cache is cacheable
ENVY_IGNORE_DEPOT: 1 # only in depot publish jobs

The first puts the cache somewhere the CI cache action can save and restore. The second belongs only in jobs that publish a depot. See GitHub Actions.

See also​