Reference

CLI Changelog

Version history for the envless CLI (@goenvless/cli).

Version history for the envless CLI, shipped in the @goenvless/cli package, which contains the terminal binary and nothing else. The runtime library ships separately as @goenvless/env, and the programmatic admin client as @goenvless/sdk, with envless-sdk as its Python and Ruby twins and envless/sdk as its PHP twin. Install it globally with npm i -g @goenvless/cli@latest and keep it up to date for the latest features, security patches, and performance improvements.

Unreleased

Next release

envless member remove and envless role delete work again. Removing a member, revoking a pending invite and deleting a role sent POST to routes the API only accepts as DELETE, so all three failed with This route does not exist!. They now send DELETE with the same body.

  • Every endpoint the CLI calls now declares its HTTP method in one place, and the API's CI checks each method and path against its route table
  • The publish preflight now recognises a leaked CLI session token by its real env_cli_ prefix

envless run and envless build no longer inject Ruby, Python or PHP loader variables. Eleven more names join the ones the CLI decrypts but never places in the child environment, because a stored value would turn write access to a variable into code execution in a Ruby, Python or PHP process. That makes twenty-six in all, listed on envless run.

Newly refused

  • Ruby: RUBYOPT, RUBYLIB, BUNDLE_GEMFILE, GEM_HOME and GEM_PATH
  • Python: PYTHONPATH, PYTHONHOME, PYTHONSTARTUP and PYTHONUSERBASE
  • PHP: PHPRC and PHP_INI_SCAN_DIR

How the refusal behaves

  • Names are matched without case, so pythonpath is refused too
  • The warning names every value that was dropped, as it already does for NODE_OPTIONS and the rest
  • An ambient value keeps its place. If a process needs one of these, set it in the shell or the CI environment rather than in Envless

envless run and envless build fetch what they need in one request. With a device login they used to resolve the workspace, then the project, then the environment list, and then fetch each environment's variables, one request after another. They now ask POST /internal/variables/pull for all of it at once and decrypt locally as before. Measured from Europe, the wait before the command starts dropped from about 1.2 seconds to about 0.4 seconds.

Details

  • Several environments in --env still take a single request, where before each one added a request
  • An ev_sk_ API key takes the same path as before, and so do envless sync and envless types
  • The spinner reads Loading variables, and a failed load reports Could not load variables (<reason>) instead of naming the step that failed. A missing workspace, project or environment, a locked workspace and a value that cannot be decrypted are reported as before
  • One run loads at most 20 environments. More fail with At most 20 environments can be loaded in one run.

Guarded commands ask for a verification code instead of failing. Destructive and access-changing actions now need a recent step-up verification. When the server refuses one with auth.step_up_required, the CLI pauses its spinner, requests a code, prompts for it, verifies it and reruns the refused request once. One code covers the rest of the run and the next hour, in the terminal and the dashboard alike.

Commands that ask

  • envless push, envless var rm and envless version restore
  • The delete subcommands of workspace, product, project and env
  • envless product update, envless env expose, envless workspace virtual-envs, envless domain claim and envless domain remove
  • envless member invite, update and remove, envless access grant and revoke, and envless role create, update and delete. Removing an invite that was never accepted is not guarded and never asks

How the prompt behaves

  • With two factor authentication on, the same field takes an authenticator code or a recovery code, and the emailed code still works
  • A wrong code is reported and asked for again, up to five times. A locked authenticator stops the loop at once
  • --yes skips the CLI's own confirmation and never the code
  • Without an interactive terminal the command fails before any code is requested, with This action needs a verification code. Run it from an interactive terminal, or do it from the dashboard. A CI job that pushes with a CLI token fails cleanly instead of hanging. Verify from the dashboard first, which elevates the same user for an hour, or turn verification codes off for the workspace under Settings, Verification Codes
  • A push refused for want of a code no longer suggests running envless sync first, since a sync would not have helped

Variable names now reserve their case. A name is stored exactly as you typed it, and every place the CLI compares one folds case first, so API_KEY and api_key are understood to be the same variable rather than two.

Names

  • Matching, sorting and lookup are case insensitive, so envless var get api_key finds API_KEY
  • envless push, envless var set and envless sync warn when two names in the same environment differ only in case. The process environment is case sensitive on macOS and Linux and case insensitive on Windows, so the same code reads them differently on each
  • They also warn when a name is not UPPER_SNAKE_CASE, because process.env.API_KEY never sees api_key. Silence it with ENVLESS_NO_NAME_SHAPE_WARNING=1
  • The names envless run and envless build refuse to place in a child environment are now matched case insensitively, so a differently cased ld_preload can no longer slip past the refusal

Passphrases and decryption

  • A value sealed at an older iteration count is decrypted at that count instead of failing, so variables written by an earlier release keep opening
  • envless passphrase set validates against the rules that apply when a passphrase already exists, rather than only the rules for a new one

Environments and sync

  • envless types regenerates envless-env.d.ts as the variables behind it change, instead of leaving a stale declaration in place
  • envless build, envless run and envless sync explain what they resolved and what they wrote
  • Changing only a variable's metadata now says so, rather than implying the value was rewritten
  • envless domain claim, status and verify report progress and failures more clearly

The runtime can now adopt a wrapped process. envless run and envless build hand the child an ENVLESS_INJECTED manifest naming every variable they injected with its type and visibility, so @goenvless/env reads what is already there instead of fetching a second time. The manifest name is itself refused as a variable, so it cannot be spoofed.

Resolving what to act on

  • envless run, sync, build and types fall back to the workspace chosen with envless workspace use, matching what every write command already did. Previously they resolved a workspace only from a .envless file
  • envless passphrase set gains --workspace, --product, --project and --env, so it can run in a repo that names its target inline rather than through .envless

Encryption

  • A v2 envelope records the iteration count it was sealed at, and the interactive read path now honours it. Before this, anything sealed at a non-default cost failed to decrypt, which is the one case the v2 format exists to handle. var get, version get, sync, run and the passphrase check all derive per envelope and only when the cost differs

Packaging

  • The binary ships as envless.js at the package root rather than under bin/, so the published layout matches @goenvless/sdk and @goenvless/env

Correctness

  • Roughly thirty defects were fixed along the way
  • Two collection responses the management client never unwrapped are handled, so the commands reading them stopped returning empty

Initial public release. Sign in once, then do from the terminal anything you can do in the dashboard: create products, projects, environments and variables, publish and restore versions, and manage your team. Variables are encrypted on your machine before they are sent and decrypted locally when you read them, so the server never sees your plaintext.

Authentication

  • envless login runs a device-code flow through the browser. The token is saved to ~/.envless/config.json (file mode 0600, directory mode 0700)
  • envless logout revokes the session server side and wipes local credentials
  • envless whoami shows the signed-in account, falling back to the cached profile when offline

Getting set up

  • envless init is one command for a new repo. It creates the project, creates its environments, and writes .envless. Defaults to development, staging and production, override with --env. In a linked directory it prints the current binding and asks before creating a new project, defaulting to No. With no terminal attached it refuses instead of asking, unless you pass --yes
  • envless link walks an interactive picker: workspace, then product, then project, then environments. Re-running it in a linked directory shows the current binding and asks before changing it, defaulting to No, so running it again is a safe way to read the binding back
  • Both write a .envless JSON file in the directory you run them in. It is safe to commit, it contains slugs only. product is optional and only matters when the same project slug exists in more than one product

Encryption keys

  • envless passphrase set prompts for the workspace passphrase, derives the key (PBKDF2 200000 iterations plus SHA-256 into AES-GCM 256), and caches it under workspaceKeys[<workspaceId>]. Same derivation as the dashboard, so a passphrase set in the browser works here
  • envless passphrase clear wipes the cached key for this workspace. --all wipes every cached workspace key on this machine
  • A wrong passphrase is caught by trial-decrypting an existing variable, and no key is cached on failure

Products, projects, environments

  • envless product create|list|update|duplicate|delete
  • envless project create|list|update|duplicate|delete, where create takes --env a,b to add environments and --link to write .envless. rename is kept as an alias of update
  • envless env create|list|rename|duplicate|expose|delete. duplicate copies the variables too, still encrypted, and expose makes an environment publicly readable after confirming
  • Slugs are derived by the server from the name unless you pass --slug, so the CLI prints the slug that was actually stored rather than guessing

Variables

  • envless var set NAME=value [NAME=value ...] creates or updates, and takes a comma-separated --env to write several environments in one run
  • Pass a name on its own and it prompts for the value, or pipe exactly one value in with --stdin. Either way the value stays out of the process list and the shell history, which a NAME=value argument does not, so set warns when you use one
  • On an existing variable, set changes only what you passed. Omit --type, --visibility, --secret, --required or --default and those fields stay as they were
  • envless var get NAME prints one decrypted value and nothing else, so KEY=$(envless var get NAME) works
  • envless var list prints a masked table, --reveal shows the values
  • envless var rm NAME [NAME ...] asks for one confirmation covering the whole run
  • envless var history NAME shows the change events for one variable as when, what and who. It never decrypts anything, so it works while the workspace is locked
  • Options: --type (string, number, boolean), --visibility (server, client), --secret / --no-secret, --required / --no-required, --default, --stdin

Pushing a file up

  • envless push reads .env.<slug>, falls back to .env, and applies the difference against the environment. --file picks another path
  • --dry-run prints the plan and writes nothing. --prune also deletes variables that are no longer in the file, after one confirmation
  • Transactional with drift detection. If the environment changed since the values were read, nothing is written and the CLI tells you to run envless sync first
  • ENVLESS_ prefixed names and names that are not valid environment keys are skipped and reported
  • envless var import <file> is the same command under another name

Versions

  • envless publish [note] snapshots an environment, fires a webhook, and emails the team. envless version publish is the same command
  • envless version list lists every version with its label, variable count, note, author and creation date
  • envless version get <label> shows what a version holds, masked by default. --reveal decrypts the values, and is the only thing in this group that needs the workspace key
  • envless version restore <label> replaces the current variables with the snapshot in one server-side transaction

Team, roles and access

  • envless member invite|list|update|remove|resend|leave. member list shows active members and pending invites together, and remove handles either
  • envless role list|create|update|delete|permissions. permissions prints the full catalogue you can grant, and --permissions replaces a role's set rather than merging into it
  • envless access grant|revoke|list controls who can reach a private product

Workspaces and custom domains

  • envless workspace list|create|update|use|delete, plus virtual-envs to toggle the feature. use sets the default workspace for the dashboard and for commands that do not name one
  • envless domain claim|status|verify|remove for the workspace custom domain, printing the DNS records you need to add

Reading variables

  • envless sync pulls and decrypts every environment in .envless and writes one .env.<slug> per environment at mode 0600. Always suffixed, never a plain .env. envless pull is an alias
  • Idempotent. It skips the write when the variable body is unchanged, ignoring the timestamp header, so there are no spurious mtime changes and no dev-server reloads
  • envless sync --require-gitignore checks every target against git in one pass, as soon as the environments resolve. If any .env.<slug> it is about to write would be tracked, the whole run is refused there and then, before a single variable is fetched or decrypted and before any file is touched. Without the flag a tracked file only earns a warning as that environment is written
  • envless run -- <cmd> injects the decrypted variables into a child process, where Envless values win over same-named ambient ones
  • The child environment is not a plain merge. A name that changes how the child executes code is decrypted but never placed in the environment, because storing one would turn write access to a variable into code execution. The refused set is NODE_OPTIONS, NODE_PATH, NODE_REPL_EXTERNAL_MODULE, ELECTRON_RUN_AS_NODE, LD_PRELOAD, LD_LIBRARY_PATH, LD_AUDIT, DYLD_INSERT_LIBRARIES, DYLD_LIBRARY_PATH, DYLD_FRAMEWORK_PATH, PATH, BUN_INSPECT, BUN_INSPECT_CONNECT_TO and the ENVLESS_CLIENT_ENV channel. Each one that was dropped is named in a warning, and the ambient value keeps its place
  • envless build [target] -- <cmd> is the same for build commands, where target is optional and only an informational label
  • envless types emits envless-env.d.ts augmenting NodeJS.ProcessEnv, EnvlessServerEnv and EnvlessClientEnv. No key required, it only needs names, types and visibility

Cache

  • The on-disk ciphertext cache under ~/.envless/cache belongs to @goenvless/env. The runtime is what fills it and what reads it back, for 15 minutes per entry. The CLI no longer writes it and never reads it, so envless sync, envless run and envless build always fetch
  • Entries are keyed by workspace plus product plus project plus environment, so two projects sharing a slug in different products, or in different workspaces, never share a file
  • Any write clears the entry for the environment it touched, so the next @goenvless/env load sees the new value instead of a stale one. push, var set, var rm, version restore, env delete and project delete all clear
  • envless cache clear clears the entries for the current product, project and environments. --all wipes the whole cache

Shared behaviour

  • Every list command takes --json. Every delete takes --yes to skip the prompt, and refuses to run unattended without it
  • delete and duplicate accept several targets at once. Every target is resolved before anything is touched, so one bad identifier aborts the run rather than half-applying it
  • ls, rm, remove, del, add, new, copy, edit, log and rollback work as subcommand aliases
  • --workspace, --product, --project and --env accept a slug or a unique ID. --env is comma-separated where a command supports more than one

Machine and CI

  • Reads ENVLESS_TOKEN, ENVLESS_KEY, ENVLESS_PASSPHRASE, ENVLESS_PRODUCT, ENVLESS_PROJECT and ENVLESS_ENV from the environment. Silence the extras with ENVLESS_NO_UPDATE_CHECK and ENVLESS_NO_INSECURE_CACHE_WARNING
  • An ev_sk_ API key can read but not write. Commands that create, change or delete anything need a device login, so run envless login on that machine

Quality of life

  • A background update check runs against registry.npmjs.org and prints its banner on stderr, so it never pollutes envless var get or --json output. Silence it with ENVLESS_NO_UPDATE_CHECK=1
  • Clean exit on SIGINT and SIGTERM (codes 130 and 143), with the terminal cursor restored
  • Requires Node.js 20 or newer, for the Web Crypto APIs