Penv Cloud
Start free
Docs
Changelogsince 1.0.0-alpha.3

Command line changelog

Every user-facing change to the penv binary, newest first, with the release each one shipped in.

Every change you can see in the penv binary, newest first, with the release it shipped in. Everything below is the 1.0 line; for 0.16 and what replaced it, see upgrade from 0.16.

1.0.0 follows 1.0.0-rc.3, and stability lists what 1.0 keeps. Release delivery: upgrade penv.

1.0.0

19 October 2026. Release notes

Added

  • penv push sends a computed @hosts as hostsExpr; penv.cloud 1.0 stores and shows it. A server that refuses hosts or hostsExpr gets one retry without them, with a warning.
  • url() reads and rewrites parts of a URL. url($DATABASE_URL, host) reads, url($X, host=replica.internal.test) writes. The user, password, path and query stay sensitive unless [url] public opts them in. Functions.
  • json() reads, writes and builds JSON: json($X, db.hosts[0]), json($X, project_id=acme), json(username=$U, password=$P). Functions.
  • hash() hashes and signs: hash($KEY, algo=sha256, length=8), and hmac-sha256 or hmac-sha512 with key=, in hex, base64 or base64url. Functions.
  • random(kind=, length=) takes named options. Kinds are alnum, hex, base64url and uuid.
  • b64d filter: ${KEY | b64d} decodes base64, the inverse of base64.
  • [url], [hash] and [json.KEY] public lists in .penv/config.toml declassify the parts, hash keys and JSON paths you name. penv check rejects an entry that would expose a secret. Config.
  • A public read declassifies only a stored value. url() and json() reads on the public lists declassify a part of a key's stored value, never of a value an expression built, so url("https://${TOKEN}.x.test", host) stays sensitive. A URL's path is sensitive by default, since a webhook URL carries its secret there; add "path" to [url] public to opt in.
  • A computed @hosts is allowed. @hosts=url($DATABASE_URL, host) is pinned under [hosts]; penv check and a sealed run refuse a key whose hosts differ from the pin, and penv check --pin-hosts writes the pins. @hosts.
  • The editor completes and explains function arguments and suggests the closest option on a typo.

Breaking

  • random(N) is a validation failure. Write random(length=N). Upgrade notes.

1.0.0-rc.3

29 September 2026. Release candidate. Release notes

Fixed

  • Masking no longer breaks Next.js middleware. With mask = true, the generated env.ts and the penv run preload mask Response bodies through a subclass of Response; a NextResponse built on the original class then failed Next's instanceof Response check, and every request answered 500 ("Expected an instance of Response to be returned"). A response built on the original class passes instanceof Response again.

Changed

  • Comments in files penv gen writes are at most two lines each.
  • Every setting in .penv/config.toml sits under a comment of at most two lines: what it does, then the values it takes. penv gen writes one above each [targets.<name>.options] key (Values: node | vite | deno | workers (default node).) and keeps a comment you wrote there.

1.0.0-rc.2

29 September 2026. Release candidate. Release notes

Breaking

  • A CI or AWS login names its workspace by id. We take only the workspace id as the OIDC audience and in the signed AWS x-penv-cloud-org header, and penv refuses a slug in @penv= before any request when it would become either. Migration: replace the workspace slug in @penv= with the Organization ID from Settings → Organization in the console. A person's penv login is unchanged.

Added

  • penv run keeps the command's own variables. A key whose variable would change how the command runs (NODE_OPTIONS, LD_PRELOAD, BASH_ENV, PATH, PIP_INDEX_URL, YARN_*, CARGO_* and the rest, in any case), or that is not a variable name, fails the run with reserved_name (exit 3), naming the parameter. The child never inherits a CI runner's OIDC request token, its runtime, results and cache tokens, the step files (GITHUB_ENV, GITHUB_OUTPUT, GITHUB_PATH, GITHUB_STATE), INPUT_* or STATE_*.

Changed

  • penv login asks you to type the code: penv opens the approval page and prints Enter this code in the browser: ABCD-EFGH. Only approve it if you started this login. After sign-in it lists the workspaces the login reaches. A workspace's sign-in policy is printed as the workspace wrote it, and an ended login says to sign in again.
  • penv push and penv set check a write before sending it: names match ^[A-Za-z0-9_][A-Za-z0-9_.-]*$ up to 255 characters, values are up to 256 KiB, and the refusal names the key, never the value. A push of more than 1000 keys goes in batches of 1000 after every value's size is checked; --prune is refused when a push takes more than one batch. machine_change_requires_approval says to make the change as a person in the console, where it becomes a change request.
  • Each machine-login refusal says what to change: a refused CI trigger (pull_request_target) names the ones that work, an expired token says it expired, and an untrusted one points at connect a platform. unavailable is retried three times before it is named.
  • An AWS login is signed afresh for every try, since we accept a signed request once.
  • A .env reads a backtick escaped inside double quotes without a warning, as the console exports it.

Fixed

  • Arrow keys move the selection in every choice prompt, including application cursor mode (tmux, screen) and an arrow whose bytes arrive in two reads. j/k and Ctrl-P/Ctrl-N work too, and a prompt is never shown when stdin is not a terminal.

1.0.0-rc.1

27 September 2026. Release candidate. Release notes

It behaves as 1.0.0-beta.4 does; until 1.0.0 only fixes land. The installers, penv upgrade and npm i -g @penvhq/cli install it.

Added

  • The stability promise: what a 1.0 minor or patch release keeps as it is (command names, flags, exit codes, JSON output, the .env.schema grammar, .penv/config.toml keys) and what it may change. It lives in Design.md §13; stability summarises it.

Changed

  • The Copilot CLI guard is marked experimental wherever init and check describe it: Copilot publishes no schema for its permissions config, so the guard sits outside the stability promise.
  • sslmode=verify-ca on a sealed Postgres URL is documented as what it already does: it checks the server's certificate and its host name, as verify-full does.

Fixed

  • /install and a plain penv upgrade gave 1.0.0-beta.3 after 1.0.0-beta.4 shipped: the release workflow counted the old @penvhq/*@0.x package releases as plain penv releases. Only v tags count now, so they give this release.

1.0.0-beta.4

25 September 2026. Pre-release. Release notes

Working with penv.cloud

  • Every OIDC, AWS and keypair exchange asks for its credential's lifetime (ttlSeconds): 5 minutes under an agent, 15 otherwise. An agent never reuses a credential past 5 minutes.
  • Offline, only development falls back to the local value files. Any other environment stops with offline (exit 5).
  • penv refuses a command the chosen provider does not declare with unsupported, naming the provider and the capability.
  • Messages name penv.cloud, not penv-cloud.

Coding agents

Install and help

  • install.ps1 verifies the release signature with OpenSSL 1.1.1 or newer from PATH, as install.sh does.
  • A penv installed by npm is told npm i -g @penvhq/cli@<version>, because npm's latest tag holds no prerelease.
  • penv --help, penv help --json and the shell completions read one summary per command.

1.0.0-beta.3

24 September 2026. Release notes

Editors

  • penv lsp serves .env.schema over the Language Server Protocol: the problems penv check reports, completion and hover for every decorator, go-to-definition for key references, and an outline. It reads no value file and sends no request. The penv extension for VS Code runs it and ships separately.

Upgrades

  • penv upgrade [latest|next|<version>]: next follows prereleases, a version pins or steps back, and latest stays the default. Refusals: invalid_version, channel_unavailable.
  • Once a plain release exists, a prerelease no longer becomes the release /install and penv upgrade give.

Working with penv.cloud

  • A key we withhold from a person's login or a static token, on a write-only environment, is redacted: present, never missing. run and bundle refuse it unless a local layer such as .env.<env>.local supplies it, pull writes # penv:redacted KEY in place of the value, ls and why say it is withheld, and reveal refuses it.
  • When we refuse a taken project name, an org slug two workspaces share, a value we cannot decrypt, or a lowercase key name on push, penv names it: project_taken, org_ambiguous, undecryptable, name_invalid.
  • An AWS login signs the workspace in x-penv-cloud-org, which we require; without it we refused every AWS exchange.

Generated code

  • gen ts output type-checks under noUnusedLocals and noImplicitOverride.

1.0.0-beta.2

23 September 2026. Release notes

Sealed runs

  • @hosts names where a value may go. Under an AI agent, or with penv run --sealed, the command holds a placeholder and a proxy inside penv run puts the real value into requests to those hosts only. Echoed values come back as the placeholder.
  • postgres:// and redis:// URLs go through local proxies that log in with the real credential.
  • penv can seal AWS_SECRET_ACCESS_KEY: the proxy re-signs each request with SigV4.

Local-first values

  • Value files layer in the order .env, .env.local, .env.<env>, .env.<env>.local; the environment comes from @currentEnv.
  • Computed values: ${KEY}, filters, match, if, fallback, random(N), and penv() to read another environment, project or org. @assert and @rotate.
  • penv encrypt and penv decrypt keep the sensitive values beside .env.schema encrypted, with the key in the OS keychain. penv bundle packs one environment into .penv/<env>.bundle, which penv run reads with PENV_BUNDLE_KEY instead of reading from us.
  • penv why KEY says where a value comes from and how penv treats it. It does not print the value.

Checks

  • penv check reads the code: undeclared reads and unused keys are named; --strict fails on undeclared reads. A value file holding a secret that git tracks fails the check.
  • penv scan finds secret values committed to files. After a build, client output is scanned for secrets.
  • Masking is on by default in every penv run, and inside Node, Bun, Deno and Python through a preload.

Generated code and deployment

  • penv gen writes go, rust, php, java and csharp as well as ts and py. gen ts exports one env for server and client code.
  • .penv/config.toml holds every setting; target settings moved into [targets.<name>] and old .penv/targets/ folders migrate on the next gen.
  • Providers: @penv=<provider>:org/project, --provider, and [providers.<slug>] url.
  • ECS task roles, EKS IRSA and Pod Identity, an AWS Lambda layer, and a signed scratch image.

1.0.0-beta.1

22 September 2026. Release notes

Projects and environments

  • penv project ls | new | rename | rm and penv env ls | new | rename | copy | rm manage the tree from the terminal. -p <project> is optional inside a linked folder. A rename of the project a folder is linked to rewrites its @penv line. rm asks a person to type the name; an agent or a pipe gets exit 4 and the command to run.
  • penv env copy creates an environment with another one's keys and settings. Values are never copied.

Everyday

  • penv ls --env <name>. In a linked folder penv ls reads the cloud, so a deleted .env no longer lists every key as missing.
  • penv pull in a folder with no @penv line links it to a project the account already has, asking which one when there are several.
  • Styled output at a terminal: notes, warnings, refusals, arrow-key pickers, spinners on network calls, and tables. JSON, agent and piped sessions are unchanged.
  • Grouped penv --help. hook, schema and completions still work but are hidden from it.

1.0.0-alpha.4.4 to 4.6

21 September 2026.

  • A schema with a @penv line reads the cloud even when .env exists; the local file stands in only while offline.
  • penv pull writes every key it can and lists the ones it left out. A value with quotes and line breaks is written in the quote it lacks, so only a value using all three quote marks is left out. A carriage return never blocks a pull.
  • The generated TypeScript spells every key out (process.env.NAME), which bundlers can inline.
  • Messages say what happened, name the keys, and give the command that fixes it.

1.0.0-alpha.4

16 September 2026. Release notes

Push

  • penv push refuses before we hear anything, and names the fix. penv refuses a missing or valueless .env as nothing_to_push instead of creating an empty project, an --env the project does not have as no_such_environment (listing the ones that exist), and an --org that contradicts the schema header as org_mismatch instead of ignoring it.
  • penv creates a brand-new project with the environment the push is for, not only development.

Repository

  • The CLI now lives at penvhq/penvhq. The npm packages carry that repository, which is what provenance checks them against.

1.0.0-alpha.3

10 September 2026. Release notes

Signed releases

  • A penv release key signs every release checksum file, and a release stays a draft until that signature exists.
  • penv and install.sh verify a download against a key list compiled into the build. That list was first populated with this release.
  • manifest.json ships beside the binaries with every release, and this documentation is generated from it.

Coding agents

  • penv reveal under an agent session now files an approval in the console and exits 4 rather than printing anything.
  • --approval <key> redeems that approval once, bound to the person who granted it and to the agent session that asked. A wrong key never spends an approval.

Windows

  • penv run resolves a bare program name through PATHEXT, so the .cmd shims a package manager installs start on Windows.

Typed access

  • Every target option now declares its default and the values it allows.
  • penv gen --options lists them with a line of description each, and both init and gen say where your remembered answers live.

1.0.0-alpha.2

9 September 2026. Release

Installing and upgrading

  • Two installers shipped: install.sh for macOS and Linux, install.ps1 for Windows. Both compare a download against the release digest and install nothing that does not match.
  • @penvhq/cli became an npm package that runs the platform binary, for people who want npm to own it.
  • Windows arm64 joined the published builds.
  • penv.cloud became the one release address the binary reads.
  • penv upgrade arrived, along with penv completions for your shell.

Adoption

  • The one dotenv escape now round-trips, so a value written with \n comes back the way it went in.
  • penv init asks where generated files should go instead of guessing.
  • Guards and language targets work in any repository layout rather than only the shapes penv shipped with.

Typed access

  • The TypeScript target applies the defaults declared in your schema.
  • A [options] key_case setting gives you camelCase property names.

1.0.0-alpha.1

8 September 2026. Release

The first release of the Rust rewrite. Everything in it is new.

The binary and local mode

  • One committed .env.schema replaces every config file.
  • penv init writes that schema from an existing .env. penv check reports what is wrong with it, and penv ls lists what is in it.
  • penv run validates, then puts the values in the child process only.

Cloud mode

  • penv login signs a person in through a device code, and penv push sends an environment up.
  • penv pull writes a plain .env back down. penv set and penv unset change one key.
  • penv reveal prints one value.
  • penv machine enroll gives a host that can prove nothing a key of its own.
  • Four credential kinds: a platform token, an AWS signature, a bound keypair, and a plain pck_ bearer.
  • An encrypted local copy of each environment, sealed under a key in the operating system keychain and bound to the credential that fetched it.

Coding agents

  • penv detects an agent session. Its own output becomes JSON, and every sensitive value in the child's output is masked.
  • penv guard and penv hook write the rules that keep an agent's tools away from .env.

Typed access

  • Language targets for TypeScript and Python, generated from the schema rather than read at runtime.

Releases

  • One archive per target with a checksum in the release.

Next: upgrade from 0.16.