.envfiles, 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
dotenvlibraries,process.env, everything works unchanged. -
Safe to commit —
.env.upfiles can live in git. Only metadata is visible, values are encrypted. -
Cross-IDE — keys at
~/.dotenvup/identitywork across VS Code, Cursor, CLI, and any tool.
For seamless team sharing: unknownpassword.com.
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)"
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
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.
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
Full flow (read/save sequences): Safe Edit design.
| Package | Description | npm |
|---|---|---|
@dotenvup/format |
Core .env.up format parser & writer |
|
@dotenvup/node |
Drop-in dotenv replacement for Node.js |
|
@dotenvup/cli |
CLI tool (up lock, up unlock, up run) |
|
| 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 & comments<br/>Timestamps & 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
#!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
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
-
Format Specification (v1) — The authoritative
.env.upopen standard - User Guide — Commands, workflows, drift explained
- Security Model — What is encrypted, threat model
- Troubleshooting — Common errors, identity file and recovery issues
- File Type Registration — MIME types, UTI, icons for OS integration
- Roadmap — Extension hardening, native installers, OS file type registration
- Publishing to the Marketplace — How to publish the VS Code extension (maintainers)
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.
npm install -g @dotenvup/cli- Open a project that has a
.envfile - Click the lock icon in the status bar (bottom-right)
- First time: consent screen explains key storage → click "Protect My .env"
- Done —
.envis encrypted into.env.upand deleted
To unlock: click the status bar again → choose a duration → .env reappears.
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)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 stateFor 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.
npm install
npm run build
npm run test # 110 tests across format, safety, and cryptoPlanned 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 KubernetesSecret. 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, anddotenvup_runfor 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; useup 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 withUP_KEYfor tests and deploys, with no plaintext.envin logs or artifacts.
See Roadmap for current priorities and extension hardening.
MIT — see LICENSE.
