Docs
Referencesince 1.0.0-alpha.3

The variables penv reads

Every variable the penv binary and its installers read, what each one changes, and exactly what a child process started by penv run gets.

penv has no config file. Everything you can set outside a flag is here, and every row was read out of the source that uses it.

Every variable, and what it changes

VariableWhat it changesWho usually sets it
PENV_ENVThe environment a command reads, when --env says nothing. Local mode accepts only development.You, or your shell
PENV_TOKENA pck_ machine credential. It comes before the operating system keychain and every other credential source.Your pipeline or your server
PENV_URLThe address the binary talks to instead of https://penv.cloud. https anywhere, and plain http only for 127.0.0.1, localhost and [::1].Nobody, in normal use
NO_COLORAny non-empty value turns color off.You, or your terminal
CLICOLOR0 turns color off. Any other value changes nothing.You, or your terminal
AWS_ACCESS_KEY_IDWith the secret key, offers this machine's own AWS identity as a credential.The AWS runtime
AWS_SECRET_ACCESS_KEYThe other half of that pair. Both must be present.The AWS runtime
AWS_SESSION_TOKENCarried into the signed request when a role supplied one.The AWS runtime
AWS_REGIONThe STS region penv signs against.The AWS runtime
AWS_DEFAULT_REGIONThe same, read when AWS_REGION is unset. Neither means us-east-1.The AWS runtime
ACTIONS_ID_TOKEN_REQUEST_URLWith the request token, tells penv to mint a GitHub Actions token for your workspace.GitHub Actions
ACTIONS_ID_TOKEN_REQUEST_TOKENThe other half of that pair. Both must be present.GitHub Actions
CI_JOB_JWT_V2A platform token penv exchanges as it stands.GitLab CI
ID_TOKENThe same, read when CI_JOB_JWT_V2 is unset.GitLab CI
PENV_OIDC_TOKENThe same, for any other platform or a token you minted yourself.You
AGENTamp names Amp outright. Any other name on penv's list is a weaker match.Your coding agent
COPILOT_CLISet and not 0 or false names Copilot.Copilot CLI
CLAUDE_CODE_CHILD_SESSIONSet and not 0 or false names Claude Code.Claude Code
CLAUDECODESet and not 0 or false names Claude Code.Claude Code
CODEX_THREAD_IDPresent names Codex, and is the session id stamped on cloud requests.Codex
CODEX_SESSION_IDPresent names Codex, and is the session id when the thread id is unset.Codex
GEMINI_CLISet and not 0 or false names Gemini.Gemini CLI
CURSOR_SANDBOXPresent names Cursor.Cursor
CURSOR_AGENTPresent names Cursor.Cursor
CLINE_ACTIVESet and not 0 or false names Cline.Cline
ROO_ACTIVESet and not 0 or false names Roo.Roo
ROO_CLI_RUNTIMESet and not 0 or false names Roo.Roo
OR_APP_NAMEAider, in any case, names Aider.Aider
PS1A prompt holding ###PS1JSON### names OpenHands.OpenHands
AI_AGENTA shared convention. penv parses both name_version_mode and name@version.Several agents
CLAUDE_CODE_SESSION_IDThe session id stamped on every cloud request under Claude Code.Claude Code
CURSOR_TRACE_IDThe same, under Cursor.Cursor
AMP_CURRENT_THREAD_IDThe same, under Amp.Amp
AGENT_THREAD_IDThe same, under Amp, when AMP_CURRENT_THREAD_ID is unset.Amp
COPILOT_AGENT_SESSION_IDThe same, under Copilot.Copilot CLI
GIT_EDITORAn editor that cannot prompt anybody, such as true or cat, turns masking on when stdout is not a terminal too. No agent is named.Your tooling
PATHWhere penv looks for the command you gave penv run, and for a language target's toolchain.Your shell
PATHEXTWhich suffixes count as runnable on Windows, so tsc finds tsc.cmd.Windows
HOMEThe ~/.penv half of the target and guard lookup, and the cache directory on macOS and Linux.Your shell
USERPROFILEThe same ~/.penv lookup on Windows, read when HOME is unset.Windows
LOCALAPPDATAWhere the encrypted cache lives on Windows.Windows
XDG_CACHE_HOMEWhere the encrypted cache lives on Linux. Without it penv uses $HOME/.cache.Your desktop
COMPUTERNAMEThe machine name shown on a reveal approval page.Windows
HOSTNAMEThe same, read when COMPUTERNAME is unset.Your shell
HOSTThe same, read after those two. Without all three penv reads /etc/hostname, then calls itself the penv CLI.Your shell
PENV_VERSIONPins a release tag such as v1.2.3. Installers only.You
PENV_INSTALL_DIRWhere the binary lands. Installers only.You
PENV_RELEASE_BASEThe address a release is read from. Installers only. The binary carries the one address.The installer tests
PENV_TARGETInstall for another machine's target triple. install.sh only.You
PENV_ALLOW_ROOT1 installs as root. install.sh refuses by default.You
PENV_ALLOW_ELEVATED1 installs from an elevated shell. install.ps1 refuses by default.You
PENV_ADD_TO_PATH1 writes your user PATH, which is the only way to ask when you pipe install.ps1 through iex.You
SHELLWhich PATH line install.sh prints for you to paste. It edits no startup file.Your shell
TMPDIRWhere install.sh makes the scratch directory it reads the release into. Without it, /tmp.Your shell
PROCESSOR_ARCHITECTUREThe processor install.ps1 builds a target triple from, read only when the machine's own registry value is missing.Windows
PROCESSOR_ARCHITEW6432The same, and it wins over PROCESSOR_ARCHITECTURE, because an x64 shell on an ARM64 machine says AMD64.Windows

Two more variables exist in the penv repository and nothing that ships reads either one. PENV_SIGNING_KEY is the release workflow's secret, read by a signing tool that is never published. PENV_BLESS rewrites the snapshots the test suite compares against, and only the tests read it.

Detection is a ladder and the first row that matches wins, which is why two agents setting colliding variables still resolve to one name. The order and what changes under an agent are on coding agents.

Masking has a second way on that names nobody. When stdout is not a terminal and GIT_EDITOR is set to something that cannot prompt, penv records the session as non-interactive and turns masking on. That is the only thing it changes. penv reveal and penv pull still work the way they do for a person, and the credential keeps a person's lifetime.

Setting an agent variable by hand does not give you an agent's protections, and unsetting one does not take them away in any way that matters. Detection changes defaults and adds friction. Use --agent when you want the agent treatment on purpose, and read penv guard for what actually holds a harness back.

What a child process gets

penv run starts one child and hands it three things.

  1. Everything penv itself was started with. The child inherits penv's own environment as it stands.
  2. Every key the schema resolved, from .env in local mode or from the cloud in cloud mode. A key left empty in the file takes the default written on its schema line.
  3. PENV_ENV, set to the environment this run actually read, so your program can see which one it got.

Values reach that child and nothing else. penv writes no value into its own environment, and in cloud mode penv run writes no value to disk. The one file it keeps is an encrypted cache whose key lives in the operating system keychain.

What a child process does not get

  • A value for a key the cloud mints on request. Those keys are skipped, and penv says how many.
  • A value for a key that has none. penv run refuses at exit 3 before it starts anything when a required key is missing.
  • Its output back unread. When masking is on, penv pipes the child's stdout and stderr and scrubs every sensitive value out of them first.

penv removes nothing from the environment it inherits. A PENV_TOKEN in your shell reaches the child too, along with everything else your shell holds. Give a pipeline its credential on the step that needs it rather than on the whole job.

Next: error codes.