LPM-cli

Managing secrets across environments

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

lpm env is LPM's CLI 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 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

The check runs before lpm run, lpm dev, and lpm exec. Skip with --no-env-check (don't do this in CI).

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 at lpm.dev/dashboard/secrets ("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 the
#    dashboard reported, and a two-digit "match number". Verify the same
#    match number is shown on your dashboard before answering 'y' — that
#    glance is what defeats a copy-pasted pairing command from an
#    untrusted source.
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.

What about lpm vault?

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

A macOS desktop app named LPM Vault exists internally as a GUI front-end over the same storage, but it is not publicly released yet. Treat lpm env as the only documented surface for now.

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.
  • --no-env-check should never be on in CI. Schema violations are usually real bugs surfaced early.

See also