Upgrade penv
What penv upgrade does: release channels, the download, signature and digest checks, the swap, and package-manager installs.
penv upgrade # latest: what the installers give
penv upgrade next # highest version published, prereleases included
penv upgrade 1.0.0 # exactly this version, older or newer
penv upgrade --check # report what the channel holds; install nothingpenv upgrade replaces the running binary with this machine's build from the release the channel names. Installers: install the CLI.
A named version installs either way, to pin or step back. Anything else is invalid_version, and a channel we do not serve yet is channel_unavailable. latest gives plain releases only; next includes prereleases.
--check resolves this machine's target first, then prints penv <version> is current or the newer version with the command to run. JSON: current, latest and channel.
Version order
Versions compare as a numeric triple, and a prerelease sorts below its release: 1.0.0 > 1.0.0-alpha.10 > 1.0.0-alpha.2. Build metadata after + is ignored. When there is nothing to install, penv prints that it is current and exits 0 without downloading.
Release addresses
| Read | Address |
|---|---|
latest | https://penv.cloud/releases/latest |
next | https://penv.cloud/releases |
| A version | https://penv.cloud/releases/tags/<tag> |
| Download | https://penv.cloud/releases/download/<tag>/<asset> |
The address is compiled in and takes no override. A 403 or 429 from the release host is rate_limited, with the reset time when the host sends one.
Asset
The asset is the raw binary beside the archives, penv-<tag>-<triple> (.exe on Windows).
| Machine | Target triple |
|---|---|
| Linux x86_64 | x86_64-unknown-linux-musl |
| Linux arm64 | aarch64-unknown-linux-musl |
| macOS x86_64 | x86_64-apple-darwin |
| macOS arm64 | aarch64-apple-darwin |
| Windows x86_64 | x86_64-pc-windows-msvc |
| Windows arm64 | aarch64-pc-windows-msvc |
Any other machine gets unknown_target, which names these six.
Verification
penv checks the Ed25519 signature on the release's checksum file against the release keys compiled into the binary, before it reads the digest. A release with no signature is signature_missing; one no key verifies is signature_invalid.
Download penv-<tag>-<triple> and penv-<tag>-<triple>.sha256.
Download penv-<tag>-<triple>.sha256.sig and verify it against every release key this build carries; any one passes.
Read the digest for this exact asset name (matched whole, so the archive's line is never used). The line counts only as 64 hex characters.
Compare it with the sha256 of the download, then swap the file.
Success reports the old version, new version, path written, asset name, sha256 and ed25519. install.ps1 checks the digest only (install the CLI).
Swap
The new file lands as penv.new beside the running one.
| OS | Swap |
|---|---|
| macOS, Linux | One rename over the running binary, which keeps running until it exits. |
| Windows | Running binary renamed to penv.old, penv.new moved into place; the next penv command deletes penv.old. |
A failed swap that leaves the old binary in place is unwritable_binary. On Windows, when the new file does not land and the old one cannot be moved back, penv answers binary_lost: reinstall from the installer.
Package managers
penv resolves the target, reads the release and compares versions before it checks its own path. A managed install already on the latest release prints penv <version> is current and exits 0.
When there is something to install, the path is checked before the release key, and penv answers managed_install with the manager's command.
| Manager | Path that names it | Command |
|---|---|---|
| npm | /node_modules/ | npm i -g @penvhq/cli |
| Homebrew | /opt/homebrew/, /usr/local/Cellar/, /home/linuxbrew/ | brew upgrade penv |
| Nix | /nix/store/ | nix profile upgrade penv |
| winget | /Microsoft/WinGet/ | winget upgrade penv |
| Scoop | /scoop/apps/ | scoop update penv |
npm matches first, because a global npm install lands under whichever prefix installed Node, Homebrew's included. The npm command installs the latest tag; npm i -g @penvhq/cli@next follows prereleases.
Refusals
Every refusal exits 1. --check can answer only invalid_version, unknown_target, untrusted_ca_bundle, the release-reading refusals and unsigned_build, because it never downloads. Causes and fixes: error codes.
Release keys
A build trusts the key list compiled into it and reads no revocation. A compromised key stays trusted by installed binaries until you install a build without it.
Next: error codes.