Getting started

CLI, runtime and SDK

Which of the three packages to reach for, what each one can do that the others cannot, and how every envless command maps onto an SDK method.

Envless ships three independent packages: @goenvless/cli, the terminal binary, @goenvless/env, the runtime your application imports, and @goenvless/sdk, the API client. They are not layered on each other and none is a wrapper around another, so picking the right one matters.

@goenvless/cli@goenvless/env@goenvless/sdk
What it isThe envless binaryThe application runtimeThe programmatic admin client
Installed asA global binary, npm i -g @goenvless/cliA project dependency, npm i @goenvless/envA project dependency, npm i @goenvless/sdk
ImportableNo. Its only export is ./package.jsonYes, nine entrypoints: ., /server, /client, /edge, /next, /vite, /eslint, /crypto, /registerYes, one root entrypoint
Authenticates withA device session, or a machine key for the read-only commandsENVLESS_TOKEN, plus ENVLESS_KEY or ENVLESS_PASSPHRASEAn API key
RunsOn a developer machine or a CI runnerNode or Bun for the server entrypoints, the browser for /client, anywhere fetch exists for /edge, anywhere Web Crypto exists for /cryptoAnywhere fetch exists, except a browser. The transport throws there unless you pass dangerouslyAllowBrowser
Touches the filesystemYes: .env, .envless, the local cacheYes on the server. The ., /server, /register, /next and /vite entrypoints import node:fs, node:os and node:path to read .envless, the CLI config and the on-disk cache, so none of them runs on an edge runtime. /client, /edge, /crypto and /eslint import no Node builtinNever
Spawns processesYes: envless run and envless buildNeverNever
Loads variables into an appNo. It injects them into a child process (envless run, envless build), minus the names that would change how that process executes code, or writes them to a file (envless sync), from outside the appYes. This is the only package that puts variables in your import graph. load() also writes them into process.env, minus the same refused namesNo
Administers a workspaceYes, interactivelyNoYes, all of it

The SDK column holds for envless-sdk, the Python SDK, as well: the same API client with the same methods, installed with pip install envless-sdk. A Python application loads its variables with envless-env, the Python twin of @goenvless/env, or from the CLI through envless run -- python app.py or envless sync.

The same goes for envless-sdk, the Ruby SDK, installed with bundle add envless-sdk. A Rails or Rack application loads its variables with envless-env, the Ruby twin of @goenvless/env, or from the CLI through envless run -- bin/rails server or envless sync.

PHP has both too. envless/sdk is the API client, installed with composer require envless/sdk, and a Laravel, Symfony or plain PHP application loads its variables with envless/env, the PHP twin of @goenvless/env, or from the CLI through envless run -- php artisan serve or envless sync.

The dividing line is who is driving. The CLI is for a human at a terminal on a machine that holds a device session. The runtime is for the application itself, reading its own configuration at boot. The SDK is for a program holding an API key, with no terminal, no home directory and nobody to prompt.

@goenvless/env is the runtime, not "the runtime SDK". The SDK is @goenvless/sdk, a different package that never loads variables into your app.

Command to method map

Every envless command that changes workspace state has an SDK equivalent. The device operations further down do not, including logout, which revokes the session on the server.

Products, projects, environments

CLISDK
envless product listproducts.list
envless product createproducts.create
envless product updateproducts.update
envless product deleteproducts.delete
envless product duplicateproducts.duplicate
envless project listprojects.list
envless project createprojects.create
envless project rename / updateprojects.update
envless project deleteprojects.delete
envless project duplicateprojects.duplicate
envless env listenvironments.list
envless env createenvironments.create
envless env renameenvironments.update
envless env deleteenvironments.delete
envless env duplicateenvironments.duplicate
envless env exposeenvironments.expose and unexpose

Variables and versions

CLISDK
envless var listvariables.list
envless var getvariables.get
envless var setvariables.create or update
envless var removevariables.delete
envless var historyvariables.listHistory
envless pushvariables.apply
envless version listversions.list
envless version getversions.get
envless publish / version publishversions.create
envless version restoreversions.rollback

Workspace, people and integrations

CLISDK
envless whoamime.get
envless workspace listworkspaces.list
envless workspace createworkspaces.create
envless workspace updateworkspace.update
envless workspace deleteworkspace.delete
envless workspace virtual-envsworkspace.update with virtualEnvsEnabled
envless domain claimworkspace.domain.claim
envless domain statusworkspace.domain.get
envless domain verifyworkspace.domain.verify
envless domain removeworkspace.domain.remove
envless member listmembers.list
envless member invitemembers.invite
envless member updatemembers.update
envless member removemembers.remove
envless member resendinvites.resend
envless member leavemembers.leave
envless role listroles.list
envless role createroles.create
envless role updateroles.update
envless role deleteroles.delete
envless role permissionsroles.listPermissions
envless access listproducts.access.list
envless access grantproducts.access.grant
envless access revokeproducts.access.revoke

What only the CLI can do

These are device operations. They depend on a local filesystem, a terminal, or a browser round-trip, so the SDK deliberately has no equivalent.

CommandWhy it is CLI-only
envless login / logoutA device authorisation flow through the browser. The SDK's credential is an API key, which needs no flow.
envless init / linkBoth write a local .envless file that binds this directory to a project. init creates the project and its environments first.
envless sync / pullWrites a decrypted .env file to disk.
envless run / buildSpawns a child process with the decrypted variables in its environment, minus the names that would change how it executes code.
envless typesWrites envless-env.d.ts, the declaration that types both env and process.env.
envless cache clearClears the on-disk ciphertext cache, per environment or with --all.
envless passphraseCaches the workspace encryption key in local config, or wipes it.
envless workspace useChanges which workspace the local session points at. An API key is bound to one workspace, so there is nothing to switch.

envless push and envless sync do have SDK halves. The API side of push is variables.apply and the API side of sync is variables.listAll. What the SDK will not do is read or write the .env file at either end.

What only the runtime can do

@goenvless/env is the only package your application code imports, and the only one that puts decrypted values in front of that code:

EntrypointWhy neither the CLI nor the SDK replaces it
@goenvless/env/serverAn env proxy over every variable, coerced to its declared type, plus __override() for tests. Its per-variable types arrive when envless types writes envless-env.d.ts, which augments the EnvlessServerEnv interface.
@goenvless/env/clientAn env proxy filtered to client-visibility variables, hydrated synchronously from a payload the bundler injected. Reading a server-only variable throws.
@goenvless/env/edgeload({ token, project, environment, product?, passphrase? or key?, workspaceId?, endpoint?, fetch? }) for a runtime with no filesystem. It returns a plain record and imports no Node builtin, so it never writes process.env, never fills the env proxy, and keeps no cache.
@goenvless/env/cryptoencrypt, decrypt, encryptWithKey, decryptWithKey, workspaceKey, importWorkspaceKey, passphraseStrength and ENCRYPTION_PARAMETERS, on Web Crypto alone. No Node builtin and no Buffer, so it runs wherever crypto.subtle does.
@goenvless/env/registerThe only entrypoint that calls load() on import, with a top-level await. Importing . or /server auto-loads nothing, so /register is what a bare node --import @goenvless/env/register preload uses. require()ing it throws, since it cannot block.
@goenvless/env/next, /viteFramework plugins that await load() during config evaluation and inline client values into the bundle.
@goenvless/env/eslintThe no-server-import-in-client rule that keeps /server out of client files.

The root @goenvless/env entrypoint exports env, load, encrypt, decrypt, passphraseStrength and ENCRYPTION_PARAMETERS. Its env proxy is neither typed per variable nor filtered by visibility, and importing it loads nothing, so reach for /server or /client in application code.

What only the SDK can do

A couple of things the CLI has no command for, plus several it will not do conveniently:

  • Administer a workspace from inside a Cloudflare Worker, a Lambda or a container, with no binary to install and no filesystem to read. @goenvless/env/edge loads variables in those runtimes, but it administers nothing.
  • Write to a workspace in CI with nothing but an API key. The same ev_sk_ key reaches the CLI and the runtime, but every CLI command that writes refuses it with Machine keys are read only.
  • Compose many operations in one typed script, with pagination and typed errors handled for you.
  • Verify an incoming webhook signature.
  • Rotate a passphrase across both live variables and version history in one call.

What it will not do is load variables into your application. That is @goenvless/env.

Using them together

The three coexist without interfering. A common split is the CLI for humans, the runtime for the app itself, and the SDK for automation, in the same repository:

bash
# @goenvless/cli: a developer, at a terminal
npm i -g @goenvless/cli

envless login
envless link
envless run -- npm run dev
ts
// @goenvless/env: the application reading its own configuration,
// started with node --import @goenvless/env/register
import { env } from '@goenvless/env/server'

const stripe = new Stripe(env.STRIPE_SECRET)
ts
// @goenvless/sdk: scripts/provision.ts, run in CI with ENVLESS_TOKEN set to an ev_sk_ API key
import { createClient } from '@goenvless/sdk'

const envless = createClient()

const name = `preview-${process.env.PR_NUMBER}`

await envless.environments.create('api', { name, slug: name })

Do not try to feed a CLI device token to the SDK, or an API key to the CLI's write commands, which refuse it with Machine keys are read only. The API keeps the two credential types strictly separate: internal endpoints reject ev_sk_ keys with 401 and error.code auth.unauthorized (the message starts auth.api_key_not_accepted:), and the public API rejects everything else with auth.api_key_required. The runtime sits on the API-key side too: its ENVLESS_TOKEN must be an ev_sk_ machine key, and it refuses anything that is not one.

Every SDK method linked above has a Python twin and a Ruby twin under the same name in snake_case, and a PHP twin under exactly the same name, documented under Python, Ruby and PHP.