LPM CLI

Managing secrets across environments

Store, share, and validate per-environment secrets with lpm env.

lpm env is LPM CLI's surface for environment-scoped secrets. It stores values in encrypted form on disk, organizes them per-environment (dev / staging / production), and validates them against an env schema declared in lpm.json. This guide walks through the full lifecycle.

1. Set a few vars

lpm env set DATABASE_URL=postgres://localhost/myapp
lpm env set API_KEY=sk-... LOG_LEVEL=debug          # multiple at once

set writes to the project's vault. Values are stored encrypted; only LPM CLI can read them back.

lpm env list                  # values are masked
lpm env list --reveal         # show actual values
lpm env get API_KEY --reveal  # show one
lpm env delete API_KEY        # remove

2. Per-environment vars

Use --env=<name> to scope a var to one environment:

lpm env set --env=staging API_URL=https://staging.example.com
lpm env set --env=production API_URL=https://api.example.com
lpm env list --env=staging

lpm run start --env=staging (or lpm dev --env=staging) loads only the staging-scoped vars plus any unscoped defaults.

3. Bulk import / export

Bring in a .env-style file:

lpm env import .env.production --env=production
lpm env import .env --overwrite       # overwrite any existing keys

Export back out (e.g., for a deploy hand-off):

lpm env export .env.backup
lpm env export .env.staging --env=staging

The exporter writes a plain .env format — be careful where the file lands. .env.backup should be in .gitignore.

4. Declare a schema

Project-shared validation goes in lpm.json:

lpm.json
{
  "envSchema": {
    "vars": {
      "DATABASE_URL": { "required": true, "format": "url" },
      "API_KEY":      { "required": true, "secret": true },
      "LOG_LEVEL":    { "default": "info", "pattern": "^(trace|debug|info|warn|error)$" },
      "PORT":         { "default": "3000", "format": "port" }
    }
  }
}

Per-var fields:

FieldEffect
requiredFail if not set
formatBuilt-in validator. One of: url, email, port, boolean, integer, hostname, ip
patternRegex the value must match
defaultDefault value if unset
secretTreat as sensitive — masked in logs and lpm env list

Check every discovered environment against the schema:

lpm env check
lpm --json env check | jq -e '.success'

The command exits non-zero when any environment is invalid in both human and JSON modes. JSON retains the per-environment details and sets "success": false, so automation can inspect the structured result as well as the process status.

The same lpm.json > envSchema validation runs for the selected environment before lpm run, lpm dev, lpm <file>, and lpm exec. Skip it with --no-env-check only when you deliberately want to run with an invalid environment.

Compare the default vault with .env.example

lpm env validate does not use the schema. It compares the key names in the default local vault with .env.example; it does not inspect named environments or remote cloud state.

lpm env validate
lpm env validate --strict
lpm --json env validate | jq -e '.valid'

Without --strict, validation requires every .env.example key and allows extra keys in the default local vault. With --strict, extra default-vault keys also make the result invalid. Invalid results exit non-zero in both human and JSON modes; JSON sets both "success" and "valid" to false and retains the required, present, missing, and extra arrays for structured inspection.

5. Named environments with inheritance

For projects with many .env.* variants:

lpm.json
{
  "environments": {
    "base":    { "file": ".env" },
    "staging": { "extends": "base", "file": ".env.staging" },
    "preview": { "extends": "staging", "file": ".env.preview" }
  }
}

extends chains resolve transitively. lpm dev --env=preview loads .env, then .env.staging on top, then .env.preview on top of that — last write wins. Useful for "preview is staging plus a few overrides."

6. Onboarding a new contributor

For a teammate joining a project that uses cloud sync (Pro/Org):

git clone <repo>
cd <repo>

# 1. Generate a pairing code in the LPM.dev Registry dashboard ("Pair device").
#    The dashboard will prompt you to re-enter your password (or your TOTP
#    code if you have MFA enrolled) before it issues the code — this
#    step-up check is what keeps an unattended unlocked browser from
#    silently pairing a new device on your behalf. Then redeem the code
#    on this machine. One-time per machine.
#
#    The CLI prints the browser-key fingerprint, the device label, and an
#    eight-digit comparison number from the P-256 ECDH exchange. Make sure
#    that the dashboard shows the same number before you answer 'y'.
lpm env pair <code>

# 2. Pull the latest secrets — decrypted locally with the wrapping key
#    that pairing just installed in this machine's OS keychain.
lpm env pull

lpm install
lpm dev

The vault field in lpm.json is what links the working directory to the right cloud vault — see Secrets vault — Per-project identity. It's a UUID, not a secret; it ships with the repo so every clone resolves to the same vault.

For teams without cloud sync, hand secrets over out-of-band and use the import flow instead:

lpm env import .env.shared

Either way, the schema (lpm.json > envSchema) catches missing vars before runtime. The secret: true flag prevents accidental leaking into logs.

7. Rotate organization encryption after member removal

If you remove an organization member, rotate each env project that granted access to the member:

lpm env rotate-key --org <org-slug>

Run the command as an organization owner or administrator. Use a machine that can pull the current organization env project.

The command preserves all environments and creates a fresh content key. It wraps that key for the complete current member set.

The server compares the remote version, member set, and sharing-key fingerprints before it commits. A conflict leaves the previous remote state unchanged.

The rotation stops the removed key from decrypting future ciphertext. It cannot erase secrets or keys that the former member already copied.

What about lpm vault?

The supported CLI surface for secrets is lpm env. "Vault" elsewhere in the docs refers to the underlying storage layer — OS keychain + encrypted file fallback + cloud-sync infrastructure. See Secrets vault for the storage and encryption design.

Common pitfalls

  • .env.backup and .env.shared should be in .gitignore. lpm env export writes plaintext — committing it defeats the encryption.
  • --reveal shows real values. Don't pipe lpm env list --reveal into a CI log. Use it locally for verification only.
  • Keep validation details in automation. Exit status is sufficient to fail CI, while success, valid, and the result arrays explain what needs remediation.
  • Rotation uses remote compare-and-swap. Personal and organization rotations preserve all named environments. If the remote version changes, resolve the conflict and retry.
  • Organization wraps are state-bound. A stale key fingerprint or content-key version fails closed. The dashboard shows Needs share.

See also