Skip to content
CLI guide

Your .env secrets,one command away

The productenv CLI signs in through your browser, links a folder to a project, and pulls or pushes .env files for any environment, on your laptop or in a pipeline. This guide walks through every command it has.

  • Node.js 22.12+
  • npm package: productenv
  • Interactive or CI
Quick start
npm i -g productenv
productenv login
πŸ” Logged in successfully as jane@example.com
productenv init
productenv pull
βœ… Pulled 14 secrets from api-gateway/development β†’ .env
# edit .env, then review and upload the change
productenv push

Install

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
npm i -g productenv
pnpm
pnpm add -g productenv
npx (no install)
npx productenv --help

With npx, put it in front of every command in this guide, for example npx productenv login. Check that everything is in place:

Terminal
node --version
v22.12.0
productenv --version
2.0.0
productenv help pull

Help is built in

productenv --help lists every command and the global options; productenv help <command> (or productenv <command> --help) shows one command's flags.

Sign in

No passwords or API keys in your shell: the CLI borrows your dashboard session through the browser and keeps its own token.

  1. Run the login command

    productenv login starts a short-lived server on 127.0.0.1 (port 4567, or the next free one) and opens your browser.
  2. Authorize in the dashboard

    Sign in if needed. The Authorize ProductEnv CLI screen shows the account and the computer asking for access. Click Authorize.
  3. 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.
Terminal
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.com

One 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 …. Use productenv login --force to 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

Terminal
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.

Pull secrets into .env

productenv pull downloads an environment's secrets and writes them to .env in the current folder.

~/code/api-gateway
productenv pull
βœ… Pulled 14 secrets from api-gateway/development β†’ .env
.env
DATABASE_URL=postgres://app:app@localhost:5432/api_gateway
REDIS_URL=redis://localhost:6379
JWT_SECRET=dev-only-not-a-real-secret
LOG_LEVEL=debug

An existing file is never overwritten silently. In a terminal you are asked first; pass --force to skip the question.

Terminal
productenv pull
? .env already exists. Overwrite it? (y/N)
productenv pull --force
βœ… Pulled 14 secrets from api-gateway/development β†’ .env

Choose what and where

Terminal
# 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'
  • --env picks the environment; without it the linked default is used.
  • --output picks the file (default .env) and --format switches between dotenv and json.
  • --stdout prints 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.

Push local changes

productenv push uploads your local .env to the environment. It always shows what will change, by key name only, and asks before writing.

~/code/api-gateway
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
  • + KEY is new, ~ KEY has a different value, - KEY will be deleted (replace mode only). Values are never printed.
  • Use --file to push another file and --env for 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

Push modes
ModeWhat it doesUse it when
--mode mergedefaultCreates 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 replaceMakes 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.
Terminal
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.

Switch environments

New projects start with development, staging, production environments, with development as the default. Every command that reads or writes secrets takes --env.

~/code/api-gateway
productenv envs
Environments of acme/api-gateway (from .productenv.json)

ENVIRONMENT  SECRETS  STATUS
development  14       default
staging      16
production   18

Work with one environment, once

Terminal
productenv pull --env staging --output .env.staging
productenv push --env staging --file .env.staging

Change the folder's default

Re-run init with another --env. From then on, a bare productenv pull uses it.

Terminal
productenv init --org acme --project api-gateway --env staging
βœ… Updated .productenv.json: acme/api-gateway (#12), default environment staging

Locked 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.

Use in CI/CD

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:

  1. the --token <token> flag;
  2. the PRODUCTENV_TOKEN environment variable;
  3. the session saved by productenv login in ~/.productenv/config.json.

Get a token for CI

  1. 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. Run productenv login with it once.
  2. Store the token as a CI secret

    Copy the token value from ~/.productenv/config.json into your CI provider's secret store as PRODUCTENV_TOKEN (on GitHub: Settings β†’ Secrets and variables β†’ Actions).
  3. Pass every choice as a flag

    Without a terminal there are no prompts: pull needs --force to overwrite a file, push needs --yes, and init needs --org, --project and --env. A committed .productenv.json provides 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

.github/workflows/deploy.yml
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 build

Any other CI

Terminal
# 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 --force

Commands 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.

Command reference

Every command of productenv, with its main flags. Global options work with any command.

productenv commands
CommandWhat it doesMain flags
productenv loginOpens the dashboard in your browser to authorize the CLI, then saves a 90-day CLI token to ~/.productenv/config.json.
  • -p, --port <port>Local callback port (default 4567; the next free one if busy)
  • -f, --forceLog in again even if a session already exists
productenv logoutRevokes the CLI token on the server and deletes the local session.None
productenv whoamiShows the signed-in email and your organizations with your role.
  • --jsonMachine-readable output
productenv orgsLists your organizations with your role and plan.
  • --jsonMachine-readable output
productenv projectsLists an organization's projects with their ids and environment types (* default, πŸ”’ locked).
  • --org <name>Organization (defaults to the linked one)
  • --jsonMachine-readable output
productenv initLinks the current folder to a project by writing .productenv.json. Missing flags are asked interactively.
  • --org <name>Organization
  • -p, --project <id>Project id or name
  • -e, --env <type>Default environment type for this folder
productenv pullDownloads an environment's secrets into a file (.env by default) or to stdout.
  • -e, --env <type>Environment type (defaults to the linked default)
  • -p, --project <id>Project id, or name together with --org
  • --org <name>Organization, for project names and defaults
  • --output <file>File to write (default .env)
  • --format <format>dotenv (default) or json
  • -f, --forceOverwrite the output file without asking
  • --stdoutPrint the secrets instead of writing a file
productenv pushUploads a .env file after showing a key-only diff and asking for confirmation.
  • --file <file>.env file to upload (default .env)
  • -e, --env <type>Environment type (defaults to the linked default)
  • -p, --project <id>Project id, or name together with --org
  • --org <name>Organization, for project names and defaults
  • --mode <mode>merge (default) upserts keys; replace also deletes keys missing from the file
  • -y, --yesPush without asking for confirmation
productenv envsLists the linked project's environment types with secret counts and status.
  • --org <name>Organization (defaults to the linked one)
  • -p, --project <id>Project id or name (defaults to the linked one)
  • --jsonMachine-readable output
productenv helpShows help for a command (same as productenv <command> --help).None

Global options

Global options
OptionDescription
--token <token>Use this CLI token instead of the saved session (env PRODUCTENV_TOKEN)
--jsonMachine-readable output for whoami, orgs, projects and envs
--verbosePrint 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, --versionPrint the CLI version
-h, --helpShow help (also productenv help <command>)

Files the CLI uses

Files
FileContents
~/.productenv/config.jsonYour 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.jsonThe folder's organization, project id and name, and default environment. No secrets: commit it.

Troubleshooting

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 --token flag, no PRODUCTENV_TOKEN variable and no saved session in ~/.productenv/config.json.

    Fix: Run productenv login on your machine. In CI, set PRODUCTENV_TOKEN from 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 login again. In CI, update the PRODUCTENV_TOKEN secret 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 --verbose to 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.json in the current folder and no --project flag.

    Fix: Run productenv init in the project folder (or cd into it), or pass --project and --env.

  • Output file already exists

    exit 1
    ❌ .env already exists. Use --force to overwrite it.

    Why: pull never 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: push ran without an interactive terminal (CI, a script, a pipe), so it cannot ask.

    Fix: Review the diff in a terminal first, then add --yes in automation.

  • Environment locked

    exit 4
    ❌ This environment is locked, please subscribe to the organization premium plan

    Why: 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 project

    Why: 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 --env value does not match an environment type of the project.

    Fix: Run productenv envs to 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: push accepts KEY=value lines (optionally prefixed with export), quoted and multi-line values, comments and blank lines. Anything else is rejected.

    Fix: Fix the line in the file, or point --file at the right one.

  • Login timed out or was cancelled

    exit 1
    ⏰ Login timed out

    Why: The browser was not authorized within 120 seconds, or you clicked Cancel (the CLI then prints ❌ Login cancelled).

    Fix: Run productenv login again 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, --project and --env, or commit .productenv.json so the CLI knows the project.

Exit codes

Exit codes
CodeMeaning
0Success
1Invalid usage, validation error, aborted prompt or any other failure
2Network error: the API could not be reached
3Not logged in, or the session expired or was revoked
4Forbidden: a locked environment or a read-only role
5Not found: unknown organization, project or environment

Security notes

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 --verbose never prints tokens or values.
  • Private files. pull creates files readable by you only, and the token in ~/.productenv/config.json sits 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):

.gitignore
# Local secrets pulled by productenv
.env
.env.*
!.env.example

Revoke a CLI token

  • productenv logout revokes the token on the server right away.
  • Signing in with productenv login on 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.