Environment Variables
Everything envy reads from or writes to the environment. Most projects set none of these.
Read by envy
| Variable | Effect |
|---|---|
ENVY_CACHE_ROOT | Cache root. Same as --cache-root, and the highest-priority tier. Read by the binary and by the committed bootstrap scripts. |
ENVY_MIRROR | Where to download envy releases from. Overrides the @envy mirror directive. https:// and s3:// both work. |
ENVY_IGNORE_DEPOT | Set to skip depot lookups and build from source. Same as --ignore-depot, honored by sync, install, package, and export. |
ENVY_NO_REEXEC | Set to stop envy from re-executing into the version the manifest pins. Debugging only. |
ENVY_FETCH_BUDGET_MS | How 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_ATTEMPTS | Ceiling 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_MS | Base 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
| Variable | Set by | Value |
|---|---|---|
ENVY_PROJECT_ROOT | envy run, the shell hook, and each deployed product script | The governing manifest's directory. A product script stamps it as a hop relative to its own bin directory, and only for a root manifest. |
PATH | envy run, the shell hook, and each deployed product script | The 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.
| Variable | Effect when set to 1 |
|---|---|
ENVY_SHELL_HOOK_DISABLE | The hook does nothing. Useful in scripts and in CI where the hook is installed but unwanted. |
ENVY_SHELL_NO_ENTER_EXIT_ANNOUNCE | No entering/leaving messages on stderr. |
ENVY_SHELL_NO_ICON | No 🦝 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", orset ENVY_CACHE_ROOT=C:\envy-cacheincmd. ENVY_CACHE_ROOTandENVY_MIRRORare read bybin\envy.batas well as by the binary, so they steer the bootstrap download too.- The hook variables apply to the PowerShell hook.
cmd.exehas no hook, so nothing there reads them.
Cache root precedence
Highest to lowest:
--cache-rootorENVY_CACHE_ROOT. Must be absolute.- A
.envy-cache-localor.envy-cache-sharedmarker written byenvy cache --local/--shared. @envy cache-modein the manifest.@envy cache-localbeing present at all, which means local. Relative to the manifest's directory, with no expansion of any kind.- The platform default.
| Platform | Default |
|---|---|
| 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
ENVY_MIRROR@envy mirrorin the manifest- 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
| Variable | Where it matters |
|---|---|
HOME, XDG_CACHE_HOME, LOCALAPPDATA, USERPROFILE | Default cache root, and the ~ in .luarc.json paths. |
TERM | Whether output gets a live TUI or plain lines. |
LANG, LC_ALL, LC_CTYPE | Whether 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
- Global flags for the CLI equivalents.
- The Cache for what lives under the cache root.
- Manifest Reference for the
@envydirectives.