Upgrade from 0.16
Move from penv 0.16 to 1.0: export values, remove 0.16, install 1.0, push each environment, and the mapping from every 0.16 file, command and variable.
penv 1.0 is one static binary; 0.16 was a Node launcher with a downloaded engine, value files and plugins, and none of it carries over. You export one plain file per environment from 0.16 and push each to us.
Migrate
Export each environment with 0.16 still installed.
penv generate --env development --out .env.development --allow-decrypt
penv generate --env production --out .env.production --allow-decrypt--allow-decrypt writes sealed values as plaintext; without it generate skips them and the file is incomplete.
Move the files outside your repository. They are plaintext, and a later step deletes the tree they came from.
mkdir -p ~/penv-move
mv .env.development .env.production ~/penv-move/Remove 0.16: the global launcher and its engine store.
npm rm -g @penvhq/launcher
rm -rf ~/.penv/engines ~/.penv/extensionsInstall 1.0. Windows and npm: install the CLI.
curl -fsSL https://penv.cloud/install | shWrite the schema from the development values.
cp ~/penv-move/.env.development .env
penv init
penv checkpenv init writes .env.schema, inferring a type and sensitivity per key. Review the guesses with penv check; inference rules: from a .env file.
Push the first environment.
penv login
penv pushpenv login signs you in; penv push creates the project with development and the environment it pushes, writes the @penv= header into .env.schema, sends us the values and deletes the value files it read.
Create and push each remaining environment. push reads .env and .env.<env>, so copy the file back first.
penv env new production
cp ~/penv-move/.env.production .env
penv push --env productionpenv env new creates the environment; penv refuses a push to one that does not exist as no_such_environment. Console alternative: projects and environments.
Confirm every environment lists its keys, with penv ls or in the console. Until then your plaintext copies are the only source.
penv ls --env productionDelete the 0.16 leftovers, then the holding directory. Keep .penv/config.toml, which 1.0 writes.
rm -f penv.config.ts penv.schema.ts .penv/env.ts
rm -rf .penv/state ~/penv-moveAlso remove any @env or #env path mapping from tsconfig.json or package.json.
The files generate --allow-decrypt writes hold every value in the clear. Delete them once every environment lists its keys, and confirm none reached a commit. A value you sealed under a local key in 0.16 has no other way across: 1.0 cannot read the penv:1: envelope format.
Configuration mapping
Nothing in the 0.16 column exists in 1.0.
| 0.16 | 1.0 |
|---|---|
penv.config.ts and defineConfig | One committed .env.schema; no config file to import. |
penv.schema.ts and .penv/env.ts | penv gen ts writes the typed accessor from the schema. |
The @env and #env alias | The target decides the import line, stored under [targets.<name>] in .penv/config.toml. |
.penv/state/records/ | Value files (.env, .env.local, .env.<env>, .env.<env>.local) in local mode; our store in cloud mode (local and cloud modes). |
.penv/state/manifest.json | None; there is no engine to pin. |
.penv/state/extensions/, local-extensions.json | None. |
.penv/state/cutover.json, rollback/ | None; penv init leaves .env in place until push. |
| The four-scope cascade | One environment per read: --env, then PENV_ENV, then the @currentEnv key, then development (precedence). |
| Providers and provider extensions | Local and cloud modes, built in; @penv=<provider>:org/project names a provider, and 1.0.0-rc.1 has one, penv (schema format). |
@penvhq/provider-penv-cloud | None; cloud mode is the binary. Never published, so nothing to remove. |
Command mapping
| 0.16 | 1.0 |
|---|---|
penv init | penv init, which writes .env.schema from .env. |
penv import <file> | penv init. |
penv generate | penv pull, which writes a plain .env from the cloud. |
penv get <key> | penv reveal; under a coding agent it needs a person's approval. |
penv set <key> [value] | penv set. |
penv fill | penv set, one key at a time. |
penv mv <from> <to> | None. |
penv pull | penv pull, reading from the cloud. |
penv push | penv push, sending to the cloud and deleting .env. |
penv rotate <key> | penv set writes a new version; @rotate in the schema sets a reminder interval. |
penv run | penv run. |
penv remove <key> | penv unset. |
penv list | penv ls. |
penv cleanup | None; there is no rollback bundle. |
penv migrate | None. |
penv encrypt, penv decrypt | Same names since 1.0.0-beta.2 (penv encrypt, penv decrypt): they encrypt the sensitive values in the value files beside .env.schema as enc:v1:, under a keychain key or PENV_LOCAL_KEY. They do not read penv:1:. |
penv key create | None in the binary. You set a customer-managed wrapping key in the console on Enterprise, or on Pro with the add-on (BYOK). |
penv validate | penv check. |
penv doctor | penv check, plus penv with no arguments, which prints the state and the next command. |
penv watch | None. |
penv artifact build | None; penv pull writes a plain .env. |
penv install | None; the installer places the binary. |
penv add <package> | None. |
penv upgrade [version] | penv upgrade replaces the binary with the latest release, or with next or a named version (upgrade penv). |
New in 1.0:
| Command | Purpose |
|---|---|
penv login, penv logout | Device-code sign-in. |
penv machine enroll | Credential for a host that can prove nothing. |
penv project, penv env | Manage projects and environments from the terminal. |
penv gen | Typed access per language. |
penv guard, penv hook | Keep a coding agent out of .env. |
penv gen is not 0.16's penv generate. gen writes typed access for a language; the old generate wrote a value file, which penv pull does now.
Variable mapping
| 0.16 | 1.0 |
|---|---|
PENV_ENV | PENV_ENV. |
NODE_ENV as an environment fallback | # @currentEnv=$NODE_ENV in the schema header, after --env and PENV_ENV. |
PENV_HOME | PENV_INSTALL_DIR, read by the installers. There is no engine store. |
PENV_KEY_* | PENV_LOCAL_KEY, one 64-hex key for enc:v1: values; it cannot open penv:1:. |
PENV_DELIVERY, PENV_SNAPSHOT, PENV_RUN, PENV_SCHEMA_HARVEST, PENV_DEBUG | None. |
PENV_CLOUD_TOKEN | PENV_TOKEN, holding a pck_ machine credential. |
Full 1.0 list: the variables penv reads.
Encryption
| 0.16 | 1.0 | |
|---|---|---|
| Where values are sealed | On disk | By us, under your workspace's key |
| Cipher and key | AES-256-GCM, 32-byte key from PENV_KEY_<ID> or the OS keychain | Server side: encryption in the cloud |
| Format | One-line penv:1: envelope | No local envelope |
| Local copy | Not applicable | Cache: ChaCha20-Poly1305, key in the OS keychain. Opt-in enc:v1: values in value files |
No converter exists between the two; the generate --allow-decrypt export is the path across.
Random values
random(N) is a validation failure from 1.0.0. Write random(length=N); kind= is alnum, hex, base64url or uuid. Functions.
-SESSION_SECRET=random(32)
+SESSION_SECRET=random(length=32)npm packages
npm view @penvhq/cli dist-tags shows:
| Tag | Version |
|---|---|
latest | 1.0.0 |
next | Prereleases |
| Command | Installs |
|---|---|
npm i -g @penvhq/cli | 1.0.0, the release these pages describe |
npm i -g @penvhq/cli@next | The newest prerelease |
curl -fsSL https://penv.cloud/install | sh | The latest release, no tag needed |
0.16.2 is deprecated; its only executable was penv-engine. In 0.16 the penv command came from @penvhq/launcher, which is no longer on the registry.
Next: changelog.
External Secrets Operator
Point the External Secrets Operator webhook provider at GET /api/v1/bulk-read-secrets to read a whole environment in one request.
config.toml
Every section of .penv/config.toml: grammar version, local encryption, public prefixes, public lists for url, hash and json, and host pins.