The CLI is one npm package, productenv, and runs anywhere Node.js 22.12 or newer runs. Install it globally, or run it on demand with npx.
npm i -g productenvpnpm add -g productenvnpx productenv --helpWith npx, put it in front of every command in this guide, for example npx productenv login. Check that everything is in place:
node --version
v22.12.0
productenv --version
2.0.0
productenv help pullHelp is built in
productenv --help lists every command and the global options; productenv help <command> (or productenv <command> --help) shows one command's flags.
No passwords or API keys in your shell: the CLI borrows your dashboard session through the browser and keeps its own token.
Run the login command
productenv loginstarts a short-lived server on127.0.0.1(port 4567, or the next free one) and opens your browser.Authorize in the dashboard
Sign in if needed. The Authorize ProductEnv CLI screen shows the account and the computer asking for access. Click Authorize.Back to your terminal
The browser hands a token to the CLI, which saves it to~/.productenv/config.json. The browser then says You're all set! and you can close it.
productenv login
π Opening your browser to authorize the ProductEnv CLIβ¦
Waiting for you to authorize in the browser (Ctrl+C to cancel)β¦
π Logged in successfully as jane@example.comOne CLI session per account, valid 90 days
The CLI stays signed in for 90 days. Signing in on another computer signs the previous one out, because each account has a single active CLI token.
When something is different
- Already signed in? The CLI prints
π Already logged in as β¦. Useproductenv login --forceto sign in again (for example as another account). - No browser available (SSH, containers)? It prints
π Please open this URL manually: β¦; open that URL on a machine where you are signed in. - Port 4567 blocked? Choose another one with
productenv login --port 5678. - The CLI waits 120 seconds, then stops with
β° Login timed out. Clicking Cancel in the browser printsβ Login cancelled.
Check who you are, or sign out
productenv whoami
π€ jane@example.com (Jane Doe)
ORGANIZATION ROLE
acme OWNER
side_project MEMBER
productenv logout
π Logged out successfully.productenv logout revokes the token on the server and deletes ~/.productenv/config.json, even when you are offline (the local file is removed either way).
Only authorize what you started
Click Authorize only if you just ran productenv login on this computer. If the screen appears unexpectedly, click Cancel.
Linking tells the CLI which organization, project and default environment a folder belongs to, so that pull and push need no flags.
Run productenv init in your project's root folder. It asks three questions with arrow-key menus: organization, project, then the default environment.
productenv init
β Select an organization acme
β Select a project api-gateway
β Select the default environment development
β
Created .productenv.json: acme/api-gateway (#12), default environment development
Next: `productenv pull` to download secrets, `productenv push` to upload them.Prefer no prompts? Pass the answers as flags. The project can be its name or its numeric id:
productenv init --org acme --project api-gateway --env developmentThe link is a small JSON file in the current folder. Running init again updates it.
{
"organizationName": "acme",
"projectId": 12,
"projectName": "api-gateway",
"defaultEnvironment": "development"
}Commit .productenv.json
It contains no secrets. Committing it lets your teammates and your CI run productenv pull without setting anything up. init also warns you when .env is not in .gitignore.
Find organizations and project ids
productenv orgs
NAME ROLE PLAN
acme OWNER Paid
side_project MEMBER Basic
productenv projects --org acme
Projects in acme
ID PROJECT ENVIRONMENTS
12 api-gateway development*, staging, production
15 web-app development*, staging, production
* default environment Β· π lockedYou only see organizations you belong to and projects you have access to. Projects are created in the dashboard; the CLI works with existing ones.
productenv pull downloads an environment's secrets and writes them to .env in the current folder.
productenv pull
β
Pulled 14 secrets from api-gateway/development β .envDATABASE_URL=postgres://app:app@localhost:5432/api_gateway
REDIS_URL=redis://localhost:6379
JWT_SECRET=dev-only-not-a-real-secret
LOG_LEVEL=debugAn existing file is never overwritten silently. In a terminal you are asked first; pass --force to skip the question.
productenv pull
? .env already exists. Overwrite it? (y/N)
productenv pull --force
β
Pulled 14 secrets from api-gateway/development β .envChoose what and where
# another environment, into its own file
productenv pull --env staging --output .env.staging
β
Pulled 16 secrets from api-gateway/staging β .env.staging
# JSON instead of KEY=value lines
productenv pull --format json --output secrets.json
# print to stdout and pipe it (here: list the key names only)
productenv pull --stdout --format json | jq 'keys'--envpicks the environment; without it the linked default is used.--outputpicks the file (default.env) and--formatswitches betweendotenvandjson.--stdoutprints the secrets instead of writing a file. The summary line goes to stderr, so a pipe only receives the secrets.--project(an id, or a name with--org) pulls from a project that is not linked to the folder.
New files are created readable by you only (mode 0600), and the CLI warns when a .env* file it writes is not git-ignored.
productenv push uploads your local .env to the environment. It always shows what will change, by key name only, and asks before writing.
productenv push
π¦ .env β api-gateway/development (merge)
+ SENTRY_DSN
~ DATABASE_URL
1 added, 1 updated, 0 removed, 12 unchanged
β Push these changes to api-gateway/development? Yes
β
Pushed: 1 created, 1 updated, 0 deleted+ KEYis new,~ KEYhas a different value,- KEYwill be deleted (replace mode only). Values are never printed.- Use
--fileto push another file and--envfor another environment:productenv push --env staging --file .env.staging. - When nothing differs, the CLI prints
β¨ Already up to date. Nothing to push.and stops.
Merge or replace
| Mode | What it does | Use it when |
|---|---|---|
--mode mergedefault | Creates new keys and updates changed ones. Keys that exist only in ProductEnv are kept. | Adding or changing a few secrets. Safe for shared environments. |
--mode replace | Makes the environment match your file exactly: also deletes every remote key that is not in the file, in the same transaction. | Cleaning up an environment from a file you have just pulled and reviewed. |
productenv push --mode replace
π¦ .env β api-gateway/development (replace)
+ SENTRY_DSN
~ DATABASE_URL
- LEGACY_FEATURE_FLAG
1 added, 1 updated, 1 removed, 11 unchanged
? Push these changes to api-gateway/development? (y/N)Replace deletes secrets for everyone
A key missing from your file is deleted from the environment for the whole team. Run productenv pull first, read the - KEY lines carefully, and keep the confirmation: it defaults to No whenever something would be deleted. Avoid combining --mode replace with --yes outside reviewed automation.
Lines with an empty value (FEATURE_X=) are skipped, so in replace mode that key is deleted too.
Pushing needs the Owner, Admin or Member role on the project; Guests can pull but not push.
New projects start with development, staging, production environments, with development as the default. Every command that reads or writes secrets takes --env.
productenv envs
Environments of acme/api-gateway (from .productenv.json)
ENVIRONMENT SECRETS STATUS
development 14 default
staging 16
production 18Work with one environment, once
productenv pull --env staging --output .env.staging
productenv push --env staging --file .env.stagingChange the folder's default
Re-run init with another --env. From then on, a bare productenv pull uses it.
productenv init --org acme --project api-gateway --env staging
β
Updated .productenv.json: acme/api-gateway (#12), default environment stagingLocked environments
Environments marked π locked in envs or projects are not available on the organization's current plan: only the default environment is. Pulling or pushing them fails with exit code 4 until an owner upgrades the plan.
Environment names are the ones you see in the dashboard (for example qa or preview if your team created them). An unknown name lists the valid ones in the error.
In a pipeline the CLI reads its token from the PRODUCTENV_TOKEN environment variable and never prompts.
The token is looked up in this order:
- the
--token <token>flag; - the
PRODUCTENV_TOKENenvironment variable; - the session saved by
productenv loginin~/.productenv/config.json.
Get a token for CI
Sign in as the account CI should use
A dedicated CI member of your organization is best: it keeps the pipeline independent of your own logins. Runproductenv loginwith it once.Store the token as a CI secret
Copy thetokenvalue from~/.productenv/config.jsoninto your CI provider's secret store asPRODUCTENV_TOKEN(on GitHub: Settings β Secrets and variables β Actions).Pass every choice as a flag
Without a terminal there are no prompts:pullneeds--forceto overwrite a file,pushneeds--yes, andinitneeds--org,--projectand--env. A committed.productenv.jsonprovides the project.
Treat the token like a password
It grants the same access as the account that created it. Never commit it, never write it in a workflow file and never print it in logs. It expires after 90 days, and signing in again with that account (or productenv logout where it was created) revokes it: update the CI secret when that happens.
GitHub Actions
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
# .productenv.json is committed, so only the environment is needed.
- name: Fetch production secrets
run: npx productenv pull --env production --output .env --force
env:
PRODUCTENV_TOKEN: ${{ secrets.PRODUCTENV_TOKEN }}
- run: npm ci
- run: npm run buildAny other CI
# PRODUCTENV_TOKEN comes from your CI secret store
npx productenv pull --env production --output .env --force
β
Pulled 18 secrets from api-gateway/production β .env
# no .productenv.json in the repository? use the project id
npx productenv pull --project 12 --env production --forceCommands exit with a non-zero code on failure (see exit codes), so a failed pull stops the job. --json gives machine-readable output for whoami, orgs, projects and envs.
Every command of productenv, with its main flags. Global options work with any command.
| Command | What it does | Main flags |
|---|---|---|
productenv login | Opens the dashboard in your browser to authorize the CLI, then saves a 90-day CLI token to ~/.productenv/config.json. |
|
productenv logout | Revokes the CLI token on the server and deletes the local session. | None |
productenv whoami | Shows the signed-in email and your organizations with your role. |
|
productenv orgs | Lists your organizations with your role and plan. |
|
productenv projects | Lists an organization's projects with their ids and environment types (* default, π locked). |
|
productenv init | Links the current folder to a project by writing .productenv.json. Missing flags are asked interactively. |
|
productenv pull | Downloads an environment's secrets into a file (.env by default) or to stdout. |
|
productenv push | Uploads a .env file after showing a key-only diff and asking for confirmation. |
|
productenv envs | Lists the linked project's environment types with secret counts and status. |
|
productenv help | Shows help for a command (same as productenv <command> --help). | None |
Global options
| Option | Description |
|---|---|
--token <token> | Use this CLI token instead of the saved session (env PRODUCTENV_TOKEN) |
--json | Machine-readable output for whoami, orgs, projects and envs |
--verbose | Print HTTP details (method, URL, status, timing). Never tokens or secret values |
--api-url <url> | API base URL (env PRODUCTENV_API_URL, default https://api.product-env.com) |
--web-url <url> | Dashboard URL used by login (env PRODUCTENV_WEB_URL, default https://dash.product-env.com) |
-v, --version | Print the CLI version |
-h, --help | Show help (also productenv help <command>) |
Files the CLI uses
| File | Contents |
|---|---|
~/.productenv/config.json | Your CLI token, its expiry, the API and dashboard URLs and your email. The folder is created with mode 0700 and the file with 0600 (best effort on Windows). Delete it, or run productenv logout, to sign out. |
./.productenv.json | The folder's organization, project id and name, and default environment. No secrets: commit it. |
The messages you are most likely to meet, why they happen and how to fix them. Add --verbose to any command to see each HTTP request (never tokens or secret values).
Not logged in
exit 3β You are not logged in. Run `productenv login` or set PRODUCTENV_TOKEN.Why: No
--tokenflag, noPRODUCTENV_TOKENvariable and no saved session in~/.productenv/config.json.Fix: Run
productenv loginon your machine. In CI, setPRODUCTENV_TOKENfrom your secret store.Session expired or revoked
exit 3β Session expired or invalid. Run `productenv login`.Why: The token is older than 90 days, was revoked by
productenv logout, or was replaced because the same account signed in on another machine.Fix: Run
productenv loginagain. In CI, update thePRODUCTENV_TOKENsecret with the new token.Cannot reach the API
exit 2β Cannot reach ProductEnv API (https://api.product-env.com)Why: No network, a proxy or firewall in the way, or a wrong
--api-url/PRODUCTENV_API_URL.Fix: Check your connection and the URL in the message. Add
--verboseto see each request.No project linked
exit 1β No project linked to this directory. Run `productenv init` or pass --project <id> and --env <type>.Why: There is no
.productenv.jsonin the current folder and no--projectflag.Fix: Run
productenv initin the project folder (orcdinto it), or pass--projectand--env.Output file already exists
exit 1β .env already exists. Use --force to overwrite it.Why:
pullnever overwrites a file silently. In a terminal it asks first; without one it stops.Fix: Pass
--force, or write somewhere else with--output.Push needs confirmation
exit 1β Confirmation required. Re-run with --yes to push without a prompt.Why:
pushran without an interactive terminal (CI, a script, a pipe), so it cannot ask.Fix: Review the diff in a terminal first, then add
--yesin automation.Environment locked
exit 4β This environment is locked, please subscribe to the organization premium planWhy: Only a project's default environment is available until the organization upgrades its plan. Locked environments show π in
productenv envs.Fix: Use the default environment, or ask an organization owner to upgrade the plan in the dashboard.
Read-only role
exit 4β You are not allowed to modify secrets in this projectWhy: Guests can pull secrets but not push them. Owners, admins and members can push.
Fix: Ask a project owner or admin to change your role, or to push the change for you.
Unknown environment
exit 5β Environment "qa" does not exist in api-gateway. Available: development, staging, production.Why: The
--envvalue does not match an environment type of the project.Fix: Run
productenv envsto list the valid names, or create the environment in the dashboard.Invalid .env file
exit 1β .env is not a valid .env file. Expected KEY=value lines.Why:
pushacceptsKEY=valuelines (optionally prefixed withexport), quoted and multi-line values, comments and blank lines. Anything else is rejected.Fix: Fix the line in the file, or point
--fileat the right one.Login timed out or was cancelled
exit 1β° Login timed outWhy: The browser was not authorized within 120 seconds, or you clicked Cancel (the CLI then prints
β Login cancelled).Fix: Run
productenv loginagain and click Authorize. If no browser opened, use the URL the CLI prints.Login server could not start
exit 1β Could not start the local login server on port 4567: β¦Why: The CLI tries up to 20 consecutive ports from 4567 on 127.0.0.1 and none was free.
Fix: Pick another port:
productenv login --port 5678.Missing flag in CI
exit 1β Missing --org <name> (no interactive terminal to choose from).Why: Without a terminal the CLI never prompts, so every choice must come from a flag or the link file.
Fix: Pass
--org,--projectand--env, or commit.productenv.jsonso the CLI knows the project.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Invalid usage, validation error, aborted prompt or any other failure |
| 2 | Network error: the API could not be reached |
| 3 | Not logged in, or the session expired or was revoked |
| 4 | Forbidden: a locked environment or a read-only role |
| 5 | Not found: unknown organization, project or environment |
What the CLI does to keep your secrets safe, and what is up to you.
- Encrypted at rest. Secrets are stored encrypted with a per-project key that is itself encrypted by a master key (envelope encryption), and travel over HTTPS. They are only decrypted for members of the project.
- Nothing sensitive on screen. The push diff shows key names only, and
--verbosenever prints tokens or values. - Private files.
pullcreates files readable by you only, and the token in~/.productenv/config.jsonsits in a private folder. - Access is checked on every call. Removing someone from a project or organization in the dashboard cuts their CLI access at their next command.
Keep .env out of git
init and pull warn when a .env file is not ignored. Add the files to .gitignore (and keep a value-free .env.example if you like):
# Local secrets pulled by productenv
.env
.env.*
!.env.exampleRevoke a CLI token
productenv logoutrevokes the token on the server right away.- Signing in with
productenv loginon another computer revokes the previous token, since an account has one active CLI token. - Tokens expire on their own after 90 days.
- Deleting your ProductEnv account deletes all of its CLI tokens.
Leaked a token?
Run productenv login --force with that account: the new login revokes the leaked token immediately. Then update any CI secret that used it.