@dotenvup/cli

CLI tool for managing .env.up encrypted environment files


Keywords
dotenv, env, encrypted, secrets, cli, dotenvup
License
MIT
Install
npm install @dotenvup/cli@0.3.0

Documentation

DotEnvUp Logo

DotEnvUp

.env files, but with memory — and a lock.

.env encryption — an encrypted .env format (.env.up) and tooling: VS Code/Cursor extension (one-click lock/unlock), CLI (up; npm i -g @dotenvup/cli), and MCP server for Cursor. Encrypt .env, lock/unlock, run with secrets without writing .env to disk. Zero-knowledge; no code changes.

  • Encrypted at rest — values, comments, and structure are encrypted on disk. No more plaintext secrets.
  • Zero-knowledge, zero-trust — No server, no cloud. Keys stay on your machine; nobody else ever sees your secrets.
  • Comments preserved — your # Database, blank lines, and commented-out secrets survive encrypt/decrypt.
  • Metadata you've always wanted — each key tracks its origin, timestamp, version, and author.
  • Lock/unlock — one button to decrypt temporarily. Auto-locks when you're done.
  • Zero code changes — existing dotenv libraries, process.env, everything works unchanged.
  • Safe to commit.env.up files can live in git. Only metadata is visible, values are encrypted.
  • Cross-IDE — keys at ~/.dotenvup/identity work across VS Code, Cursor, CLI, and any tool.

For seamless team sharing: unknownpassword.com.

How It Works

sequenceDiagram
    participant Dev as Developer
    participant Ext as Extension / CLI
    participant FS as File System
    participant Key as ~/.dotenvup/identity

    Dev->>Ext: Click "Protect .env" (or: up import + up lock)
    Ext->>Key: Has keypair?
    alt No keypair
        Ext->>Key: Generate & save keypair (chmod 600)
        Ext-->>Dev: Show consent / explain key storage
    end
    Ext->>FS: Read .env (with all comments)
    Ext->>FS: Write .env.up (encrypted values, cleartext keys & comments)
    Ext->>FS: Verify .env.up decrypts correctly
    Ext->>FS: Delete .env
    Ext-->>Dev: Status: "Locked"

    Dev->>Ext: Click "Unlock" (or: up unlock 5m)
    Ext->>Key: Load private key
    Ext->>FS: Read .env.up → decrypt
    Ext->>FS: Write .env (atomic, with original comments)
    Ext-->>Dev: Status: "Unlocked (auto-locks in 5m)"
Loading

Before & After

graph TD
    subgraph " Before DotEnvUp"
        A[".env in project<br/>❌ Plaintext secrets on disk<br/>❌ Gitignored — no backup<br/>❌ Shared via Slack / email"]
    end
    subgraph " With DotEnvUp"
        B[".env.up committed to git<br/>✅ Values encrypted<br/>✅ Key names visible (replaces .env.example)<br/>✅ Comments & structure preserved"]
        C[".env appears only when unlocked<br/>⏱ Auto-locks after timer"]
    end
    A -->|"up import + lock"| B
    B -->|"unlock"| C
    C -->|"lock"| B
Loading

Lock command flow

Lock always persists the current .env into .env.up and removes .env. If the file has unsaved changes in the editor, a warning explains that the current editor content will be used and that unaccepted AI or other edits should be accepted first.

Lock command flow

Safe Edit (planned)

Edit .env.up in place via a virtual document — no plaintext .env on disk. Open from CodeLens/status bar → edit → save; the extension decrypts for the editor and re-encrypts on save.

flowchart TB
    subgraph Initiation[" "]
        User([User])
        User -->|"Click CodeLens / Status Bar"| Cmd["Command: safeEdit"]
        Cmd -->|"Open Virtual Doc"| Virtual["Virtual doc: dotenvup-safe:/.env"]
    end

    subgraph Provider["Safe Edit Provider"]
        Editor["VS Code Editor"]
        FS["SafeEditFSProvider"]
        Disk[("Disk: .env.up")]
        Key[(Keystore)]

        Virtual --> FS

        subgraph Read["Read flow"]
            R1["1. Read .env.up"]
            R2["2. Decrypt (memory)"]
            FS --> R1 --> Disk
            R1 --> R2 --> Key
            R2 --> Editor
        end

        subgraph Save["Save flow"]
            S1["1. Encrypt content"]
            S2["2. Update .env.up"]
            Editor -->|"Save (Cmd+S)"| FS
            FS --> S1 --> Key
            S1 --> S2 --> Disk
        end
    end
Loading

Full flow (read/save sequences): Safe Edit design.

Packages

Package Description npm
@dotenvup/format Core .env.up format parser & writer npm
@dotenvup/node Drop-in dotenv replacement for Node.js npm
@dotenvup/cli CLI tool (up lock, up unlock, up run) npm
DotEnvUp Extension VS Code / Cursor extension — local secret management Marketplace · Open VSX · .vsix
@dotenvup/mcp MCP server — status, keys, run (for Cursor/AI) npx -y @dotenvup/mcp; design
@dotenvup/secret-generator Password / passphrase generator (Web Crypto, EFF wordlist) — browsers & tooling In-repo; npm publish when ready

Local identity: encrypted identity.enc (+ recovery). Existing users: up key upgrade. Opt-in macOS Keychain (up key migrate-to-keychain) + session agent — see docs/design/KEYCHAIN_TOUCHID.md. Not “Touch ID by default.”

Cross-repo duty: Changing packages/secret-generator requires docs/SECRET_GENERATOR_SYNC.md (UnknownPassword mirror + vendor). Cursor project-context.mdc enforces this.

The .env.up Format — Open Standard (v1)

An encrypted .env with visible metadata — a "half-open envelope":

graph LR
    subgraph ".env.up file"
        H["🔓 Header — cleartext<br/>─────────────<br/>Key names &amp; comments<br/>Timestamps &amp; versions<br/>Author / Key-Id"]
        V["🔒 Values — encrypted<br/>─────────────<br/>XChaCha20-Poly1305<br/>Base64 ciphertext<br/>Requires private key"]
    end
    H --- V
    style H fill:#1e293b,stroke:#3DDC84,color:#e2e8f0
    style V fill:#1e293b,stroke:#ef4444,color:#e2e8f0
Loading
#!dotenvup v1
# Encrypted-By: @alice
# Encrypted-For: @bob, @charlie

[keys]
DB_HOST          v3  2026-02-10T08:00:00Z  @alice    staging cluster
DB_PASSWORD      v5  2026-02-15T10:30:00Z  @alice    # rotated
API_KEY          v2  2026-02-01T00:00:00Z  @alice    test key

[encrypted]
recipient:@bob    nonce:abc123... payload:SGVsbG8g...

You can see what's inside (key names, versions, timestamps) without decrypting. The actual values — and the original .env content including all comments — are encrypted per-recipient.

Full details: Format Spec · Security Model · User Guide

Key Storage

Keys are stored at ~/.dotenvup/identity — works across every IDE and the CLI.

flowchart LR
    App["Extension / CLI"] --> E
    E["1. UP_KEY env var<br/>(CI / Docker)"]
    E -->|not found| F["2. ~/.dotenvup/identity<br/>(cross-IDE, default)"]
    F -->|not found| L["3. Legacy VS Code secrets<br/>(auto-migrated)"]
    style E fill:#1e293b,stroke:#3DDC84,color:#e2e8f0
    style F fill:#1e293b,stroke:#7c3aed,color:#e2e8f0
    style L fill:#1e293b,stroke:#64748b,color:#e2e8f0
Loading

Documentation

Install

VS Code / Cursor Extension (recommended)

Extension ID: dotenvup.dotenvup — install from VS Code Marketplace or Open VSX (Cursor, VSCodium). In the editor: Extensions → search “.env” or “DotEnvUp” or paste the extension ID. (Display name: “.env Up (DotEnvUp)” so search-from-start finds it.)

If search doesn’t find it, use the links above or see AGENTS.md. Alternatively, download a .vsix from Releases and use Extensions...Install from VSIX....

Once installed, click the status bar button to protect your .env — no CLI needed.

CLI

npm install -g @dotenvup/cli

Quick Start

Extension (one click)

  1. Open a project that has a .env file
  2. Click the lock icon in the status bar (bottom-right)
  3. First time: consent screen explains key storage → click "Protect My .env"
  4. Done — .env is encrypted into .env.up and deleted

To unlock: click the status bar again → choose a duration → .env reappears.

CLI

up init            # Generate keypair (stored at ~/.dotenvup/identity)
up import .env     # Encrypt .env → .env.up (comments preserved!)
up lock            # Delete plaintext .env
up unlock 5m       # Decrypt for 5 minutes (with original comments)
up run -- npm start  # Run with decrypted env vars (no file on disk)

Automation and AI Agents

Use up run -- <command> to run any command with decrypted env vars — no .env file is written to disk.

up run -- npm test
up run -- npm start
up status --json        # Machine-readable lock state

For scripts, CI, and AI coding agents, see AGENTS.md.

Agent-specific context files: CLAUDE.md (Claude Code), GEMINI.md (Google Gemini). Claude Code plugin: add this repo as a marketplace and install the dotenvup plugin — see docs/CLAUDE_CODE.md. Cursor plugin: this repo is also a Cursor plugin bundling the DotEnvUp skill — see docs/CURSOR.md.

Development

npm install
npm run build
npm run test    # 110 tests across format, safety, and crypto

Backlog

Planned work (not yet scheduled):

  • Kubernetes controller — Cluster-side controller that decrypts .env.up (e.g. from a Custom Resource or annotated Secret) and creates/updates a standard Kubernetes Secret. Same idea as Sealed Secrets: one format for local dev (DotEnvUp) and GitOps in-cluster.
  • MCP (Model Context Protocol)Implemented. @dotenvup/mcp exposes dotenvup_status, dotenvup_keys, and dotenvup_run for Cursor and other MCP clients. Use DotEnvUp: Copy MCP config for Cursor from the command palette, or see docs/design/MCP_SERVER.md.
  • Lock for Agent — Explicit “lock for agent” flow and docs: ensure agents (CI, AI coders) never persist plaintext .env; use up run -- and clear guidance for when to lock after agent edits.
  • Ready-to-use CI/CD — GitHub Actions, GitLab CI, and generic shell scripts that use up run -- or decrypt with UP_KEY for tests and deploys, with no plaintext .env in logs or artifacts.

See Roadmap for current priorities and extension hardening.

License

MIT — see LICENSE.