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.
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
v0.0.5
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_HOMEandGEM_PATH - Python:
PYTHONPATH,PYTHONHOME,PYTHONSTARTUPandPYTHONUSERBASE - PHP:
PHPRCandPHP_INI_SCAN_DIR
How the refusal behaves
- Names are matched without case, so
pythonpathis refused too - The warning names every value that was dropped, as it already does for
NODE_OPTIONSand 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
--envstill take a single request, where before each one added a request - An
ev_sk_API key takes the same path as before, and so doenvless syncandenvless types - The spinner reads
Loading variables, and a failed load reportsCould 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.
v0.0.4
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 rmandenvless version restore- The
deletesubcommands ofworkspace,product,projectandenv envless product update,envless env expose,envless workspace virtual-envs,envless domain claimandenvless domain removeenvless member invite,updateandremove,envless access grantandrevoke, andenvless role create,updateanddelete. 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
--yesskips 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 syncfirst, since a sync would not have helped
v0.0.3
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_keyfindsAPI_KEY envless push,envless var setandenvless syncwarn 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, becauseprocess.env.API_KEYnever seesapi_key. Silence it withENVLESS_NO_NAME_SHAPE_WARNING=1 - The names
envless runandenvless buildrefuse to place in a child environment are now matched case insensitively, so a differently casedld_preloadcan 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 setvalidates against the rules that apply when a passphrase already exists, rather than only the rules for a new one
Environments and sync
envless typesregeneratesenvless-env.d.tsas the variables behind it change, instead of leaving a stale declaration in placeenvless build,envless runandenvless syncexplain 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,statusandverifyreport progress and failures more clearly
v0.0.2
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,buildandtypesfall back to the workspace chosen withenvless workspace use, matching what every write command already did. Previously they resolved a workspace only from a.envlessfileenvless passphrase setgains--workspace,--product,--projectand--env, so it can run in a repo that names its target inline rather than through.envless
Encryption
- A
v2envelope 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 thev2format exists to handle.var get,version get,sync,runand the passphrase check all derive per envelope and only when the cost differs
Packaging
- The binary ships as
envless.jsat the package root rather than underbin/, so the published layout matches@goenvless/sdkand@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
v0.0.1
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 loginruns a device-code flow through the browser. The token is saved to~/.envless/config.json(file mode0600, directory mode0700)envless logoutrevokes the session server side and wipes local credentialsenvless whoamishows the signed-in account, falling back to the cached profile when offline
Getting set up
envless initis 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--yesenvless linkwalks 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
.envlessJSON file in the directory you run them in. It is safe to commit, it contains slugs only.productis optional and only matters when the same project slug exists in more than one product
Encryption keys
envless passphrase setprompts for the workspace passphrase, derives the key (PBKDF2 200000 iterations plus SHA-256 into AES-GCM 256), and caches it underworkspaceKeys[<workspaceId>]. Same derivation as the dashboard, so a passphrase set in the browser works hereenvless passphrase clearwipes the cached key for this workspace.--allwipes 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|deleteenvless project create|list|update|duplicate|delete, wherecreatetakes--env a,bto add environments and--linkto write.envless.renameis kept as an alias ofupdateenvless env create|list|rename|duplicate|expose|delete.duplicatecopies the variables too, still encrypted, andexposemakes 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--envto 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 aNAME=valueargument does not, sosetwarns when you use one - On an existing variable,
setchanges only what you passed. Omit--type,--visibility,--secret,--requiredor--defaultand those fields stay as they were envless var get NAMEprints one decrypted value and nothing else, soKEY=$(envless var get NAME)worksenvless var listprints a masked table,--revealshows the valuesenvless var rm NAME [NAME ...]asks for one confirmation covering the whole runenvless var history NAMEshows 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 pushreads.env.<slug>, falls back to.env, and applies the difference against the environment.--filepicks another path--dry-runprints the plan and writes nothing.--prunealso 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 syncfirst ENVLESS_prefixed names and names that are not valid environment keys are skipped and reportedenvless 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 publishis the same commandenvless version listlists every version with its label, variable count, note, author and creation dateenvless version get <label>shows what a version holds, masked by default.--revealdecrypts the values, and is the only thing in this group that needs the workspace keyenvless 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 listshows active members and pending invites together, andremovehandles eitherenvless role list|create|update|delete|permissions.permissionsprints the full catalogue you can grant, and--permissionsreplaces a role's set rather than merging into itenvless access grant|revoke|listcontrols who can reach a private product
Workspaces and custom domains
envless workspace list|create|update|use|delete, plusvirtual-envsto toggle the feature.usesets the default workspace for the dashboard and for commands that do not name oneenvless domain claim|status|verify|removefor the workspace custom domain, printing the DNS records you need to add
Reading variables
envless syncpulls and decrypts every environment in.envlessand writes one.env.<slug>per environment at mode0600. Always suffixed, never a plain.env.envless pullis 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-gitignorechecks 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 writtenenvless 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_TOand theENVLESS_CLIENT_ENVchannel. 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, wheretargetis optional and only an informational labelenvless typesemitsenvless-env.d.tsaugmentingNodeJS.ProcessEnv,EnvlessServerEnvandEnvlessClientEnv. No key required, it only needs names, types and visibility
Cache
- The on-disk ciphertext cache under
~/.envless/cachebelongs 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, soenvless sync,envless runandenvless buildalways 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/envload sees the new value instead of a stale one.push,var set,var rm,version restore,env deleteandproject deleteall clear envless cache clearclears the entries for the current product, project and environments.--allwipes the whole cache
Shared behaviour
- Every list command takes
--json. Every delete takes--yesto skip the prompt, and refuses to run unattended without it deleteandduplicateaccept several targets at once. Every target is resolved before anything is touched, so one bad identifier aborts the run rather than half-applying itls,rm,remove,del,add,new,copy,edit,logandrollbackwork as subcommand aliases--workspace,--product,--projectand--envaccept a slug or a unique ID.--envis comma-separated where a command supports more than one
Machine and CI
- Reads
ENVLESS_TOKEN,ENVLESS_KEY,ENVLESS_PASSPHRASE,ENVLESS_PRODUCT,ENVLESS_PROJECTandENVLESS_ENVfrom the environment. Silence the extras withENVLESS_NO_UPDATE_CHECKandENVLESS_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 runenvless loginon that machine
Quality of life
- A background update check runs against
registry.npmjs.organd prints its banner on stderr, so it never pollutesenvless var getor--jsonoutput. Silence it withENVLESS_NO_UPDATE_CHECK=1 - Clean exit on
SIGINTandSIGTERM(codes 130 and 143), with the terminal cursor restored - Requires Node.js 20 or newer, for the Web Crypto APIs