Run your app locally
penv run layers your value files, validates them against your schema, masks secrets in the output, and starts one child process with them.
penv run -- pnpm dev
penv run --env staging -- pnpm dev
penv why DATABASE_URLpenv run finds the nearest .env.schema upward, resolves and validates your values, and starts the command after -- with them. penv exits with the command's exit code. penv why names where a key's value comes from and whether it is masked, public or sealed; it never prints the value.
Value files
| Order | File | Scope |
|---|---|---|
| 1 | .env | Shared |
| 2 | .env.local | This machine. Skipped when the environment is test |
| 3 | .env.<env> | Shared |
| 4 | .env.<env>.local | This machine |
Later files win. A variable already set in your shell wins over every file. When your schema names a Penv Cloud project, the files layer on top of the values we serve, and penv names each cloud value a local file replaced.
Environment selection
--env, then PENV_ENV, then the key @currentEnv names, then development. See environments and precedence.
| Source | Refusal |
|---|---|
| Value files only | A name that cannot be a file name (local, schema, ../x) exits 3. With no .env.<env>, penv warns and uses the other layers |
| Penv Cloud, your login | Exit 6 for an environment your roles do not reach |
| Penv Cloud, machine credential | Exit 6 for any environment but the one bound |
Cloud values
| State | Source |
|---|---|
| Warm cache | Encrypted cache, keychain key |
| Revalidating | HEAD with the cached ETag |
| Changed | GET, then cache rewrite |
development stays fresh for 60 seconds; every other environment revalidates on each run. When penv cannot reach us, it runs development from the cache with a daily warning, or from your local files with a warning; any other environment fails at exit 5. Details: local and cloud modes.
Child process
| Property | Behaviour |
|---|---|
| Environment | Your shell's environment, overridden by the resolved values, plus PENV_ENV |
| Command lookup | A bare name is resolved along PATH (npm finds npm.cmd on Windows) |
| Disk | Nothing written, except a generated random() value, kept in .env.local (.env.test.local for test) |
| stdout | The command's output only. penv writes its messages to stderr |
| Ctrl-C | Delivered to the child |
| Killed by signal (Unix) | Exit 128 plus the signal number |
Masking
| Case | Behaviour |
|---|---|
Every penv run | Masks your sensitive values in the command's output |
| Node, Bun, Deno, Python | A preload masks them inside the process too |
| Sensitive value under 4 characters | Cannot be masked; penv warns |
--no-mask | Works only when you run penv yourself in a terminal, with no pipe and no AI agent; otherwise ignored with a warning |
--no-preload | Human only |
Variables: environment variables. Agent sessions: keep agents out of .env.
Validation
A missing or invalid value, or a failed @assert, stops the run at exit 3, naming the key, before the command starts. An empty key takes the default on its schema line.
| Condition | Behaviour |
|---|---|
| Drift: a key with a value and no schema block | Runs. The value is masked and not validated. penv check names it |
A value file, no .env.schema | penv writes the schema first, using the guards installed on your machine, without prompting |
Exit codes
| Exit | Cause | Action |
|---|---|---|
| 3 | A value failed validation, or the environment name is invalid | Set the named key, or fix the name |
| 5 | Offline or no credential | Reconnect. Only a cached development or your local files run offline |
| 6 | Environment refused for this identity | Check the name, or request a role on it |
All codes: errors.
Procedure
Run penv check and fix every reported key.
Run penv run -- <dev command> for development.
Run penv why <KEY> when a value is not the one you expected.
Staging and production
Create an environment in your project, push values to it by name, and grant the role that reads it.
Typed access for your language
Generate a typed env file from your .env.schema in TypeScript, Python, Go, Rust, Java, PHP or C#, keep its settings in .penv/config.toml, and check it in CI.