Local mode and cloud mode
Your schema header and a credential decide the mode. Local files layer over cloud values, and a sealed local copy lets warm runs skip the round trip to us.
penv with no arguments prints the current mode and the value files it layers.
Modes
| Mode | Condition | Values come from |
|---|---|---|
| Local | .env.schema has no @penv= header | Value files on disk |
| Cloud | The header names a project | Us, through a sealed local copy, with your value files layered on top |
penv push moves the shared layers (.env, .env.<env>) to us, then deletes those files. *.local files stay. penv pull writes .env for development and .env.<env> otherwise.
Value files
.env -> .env.local -> .env.<env> -> .env.<env>.local (test skips .env.local)Later files win; *.local files are never pushed. In cloud mode, penv run lays them over our values and names each key replaced and its file. Environment: precedence.
Cloud run sequence
find .env.schema, nearest upward
open the encrypted local copy
still fresh? -> start the program
ask us whether anything changed
changed -> fetch, rewrite the local copy
cannot reach us -> see OfflineRevalidation is a HEAD with the last ETag, so an unchanged environment puts no values on the wire.
Cache freshness
| Environment | Fresh for | Every run |
|---|---|---|
development | 60 seconds | Starts without asking us inside the window |
| Every other name | 0 seconds | Asks us whether anything changed |
Offline
| Environment | We are unreachable |
|---|---|
development | Uses the local copy and prints one notice per day |
| Every other name | Uses your value files for that environment when any exist, with a warning; otherwise refuses with offline, exit code 5 |
Local copy
| Platform | Location |
|---|---|
| Windows | %LOCALAPPDATA%\penv\cache |
| macOS | ~/Library/Caches/penv |
| Other | $XDG_CACHE_HOME/penv or ~/.cache/penv |
penv seals the copy with ChaCha20-Poly1305 under a 32-byte keychain key, bound to the API root, the environment and the fetching credential: a development file does not open as production, nor one person's file for another. A file that will not open reads as no copy.
Hosts without a keychain
Containers and most servers have no keychain, so penv keeps no local copy there and asks us on every start. Account for it in health checks, or ship a bundle.
Bundles
penv bundle --env production # writes .penv/production.bundle, prints PENV_BUNDLE_KEY once
PENV_BUNDLE_KEY=<hex> penv run -- node server.jspenv bundle encrypts one environment into a file you commit or ship. With PENV_BUNDLE_KEY set, penv run reads it in place of us; value files and the process environment still override it. Refused for an agent.
Next: coding agents.
Schema and types
Your committed .env.schema names every key your app needs. Values stay out of the repository, and penv gen writes typed code from the schema.
Coding agents
penv detects coding agent sessions, seals and masks your values, and never prints a value to one without your approval. It cannot stop a process running as you.