@dotenc/cli
v0.9.2
Published
π Git-native encrypted environments powered by your SSH keys
Maintainers
Readme
π Git-native encrypted environments powered by your SSH keys
30-Second Example
dotenc init # pick your SSH key, choose a name
dotenc env edit alice # add your personal secrets
dotenc dev npm start # run with your encrypted envEncrypted .env.alice.enc committed.
No external services.
Uses your existing SSH keys.
Done.
Features
- π Uses the battle-tested AES-256-GCM encryption algorithm
- π Uses your existing SSH keys - no extra key management
- π Secure command running with on-the-fly decryption
- βοΈ Easy and secure environment variable editing
- π Supports multiple and extensible environments
- π€ Personal encrypted environments per developer
- π Automatic data key rotation on edits
- π‘οΈ Supports both RSA and Ed25519 SSH keys
Table of Contents
- 30-Second Example
- Features
- Why?
- Security Model
- How It Works
- Installation
- Basic Usage
- Tooling and Maintenance
- Team Collaboration
- Offboarding a Team Member
- CI/CD Integration
- Key Management
- Tips
- How dotenc compares
- When NOT to use dotenc
- License
Why?
Managing secrets and environment variables is critical for any modern application, but most solutions rely on third-party services and web dashboards. dotenc was created to solve these problems:
- No Vendor Lock-In: Your secrets and keys live in your codebase and your repository. You're never tied to a third-party provider or forced to migrate if pricing or policies change.
- Improved Security: Eliminate the risk of exposing secrets to external services. All encryption and decryption happen locally, and private keys never leave your machine.
- Zero Key Management: You already have SSH keys. dotenc uses them directly - no custom key generation, no extra files cluttering your home directory, no new workflows to learn.
- Better Developer Experience: No more juggling environment variables in a web UI or struggling to keep them in sync across branches. Everything is managed alongside your code, with simple CLI commands and full Git integration.
- Seamless Collaboration: Onboard or revoke team members with a single command. Grant or remove access per environment, and let Git handle the rest.
- Fully Auditable: Every grant and revoke is tracked within your Git history, so you always know who had access and when changes were made.
- PR-Safe Environment Changes: Environment variable updates live in the same pull request as your feature code. No more "merge β broken build β patch env β rebuild" workflow.
Security Model
- Each environment has its own randomly generated 256-bit data key.
- Data keys are encrypted per-user using their SSH public key.
- dotenc uses AES-256-GCM for authenticated encryption.
- Your repository alone is not enough to decrypt secrets.
- Access can be revoked at any time.
For a detailed breakdown of the cryptographic design, key material handling, threat model, and vulnerability reporting, see SECURITY.md.
How It Works
- dotenc detects your existing SSH keys in
~/.ssh/(Ed25519 or RSA); - Your public key is derived and stored in the project (
.dotenc/john.pub); - A unique data key is generated for each environment;
- The data key is encrypted with each authorized public key;
- Environment variables are encrypted using the data key with AES-256-GCM;
- Encrypted files (
.env.*.enc) are committed to your repository; - When running commands, variables are decrypted on-the-fly using your SSH private key.
Your SSH private keys never leave ~/.ssh/. dotenc reads them in place - nothing is copied, nothing is stored elsewhere.
Project Structure
After setup, your project will look like:
.
βββ .dotenc/
β βββ alice.pub
β βββ bob.pub
β βββ ...
βββ .env.alice.enc
βββ .env.production.enc
βββ .env.development.encEncrypted files are committed to Git. Public keys are stored inside .dotenc/. Each developer gets a personal encrypted environment (e.g., .env.alice.enc).
Installation
Homebrew (macOS / Linux)
brew tap ivanfilhoz/dotenc
brew install dotencScoop (Windows)
scoop bucket add dotenc https://github.com/ivanfilhoz/scoop-dotenc
scoop install dotencnpm
npm install -g @dotenc/cliStandalone binary
Download the latest binary for your platform from the GitHub Releases page.
Basic Usage
Setup
dotenc initThis will interactively guide you through the setup process:
- Scanning your
~/.ssh/directory for SSH keys (Ed25519, RSA, etc.); - Prompting for your username (defaults to your system username);
- Letting you choose which SSH key to use;
- Deriving the public key and storing it in
.dotenc/(e.g.,.dotenc/alice.pub); - Creating encrypted
developmentand personal environments (e.g.,.env.development.enc,.env.alice.enc).
No keys to generate. If you already have an SSH key (and you probably do), you're ready to go.
If you don't have an SSH key yet, just run ssh-keygen first - you'll want one anyway.
Creating a new environment
dotenc env create [environment]This command creates a new encrypted environment file under the specified name (e.g., .env.development.enc). Your personal environment is created automatically during init.
Environment names may contain letters, numbers, dots (.), hyphens (-), and underscores (_).
In a monorepo, cd to the target directory first, then run dotenc env create.
Listing environments
dotenc env listLists encrypted environments in the current directory. Use --all to recursively list all environments across the project tree. Use --json for machine-readable output ({ "environments": [...] }).
Editing an environment
dotenc env edit [environment]Opens your system's default editor to modify the specified environment. To set a custom editor, use the dotenc config editor command. It will take precedence over your system's default editor. In a monorepo, cd to the directory containing the file before running dotenc env edit.
Example:
dotenc config editor vimCurrently supported dotenc config key: editor.
You can include editor arguments, for example: dotenc config editor "code --wait".
Run commands on an environment
For development, the dev command loads both the shared development environment and your personal environment automatically:
dotenc dev <command> [...args]Example:
dotenc dev node app.jsFor explicit environment control, use run:
dotenc run --env <environment> <command> [...args]
# or
dotenc run -e <environment> <command> [...args]Example:
dotenc run -e production node app.jsYou can also specify multiple environments:
dotenc run -e base,production node app.jsIn the example above, production will override any variables also present in base.
If you want run to fail when any selected environment cannot be loaded, use strict mode:
dotenc run --strict -e base,production node app.jsIn a monorepo, run merges environment files from the project root down to the current directory (local values win). To load only from the current directory and skip ancestor directories:
dotenc run --local-only -e staging node app.js
dotenc dev --local-only npm startChecking your identity
dotenc whoamiShows your name, active SSH key, fingerprint, and the environments you have access to in this project.
Tooling and Maintenance
CLI updates
dotenc updateRuns the appropriate update flow for your installation method (Homebrew, Scoop, npm, or manual binary instructions).
Editor integration helpers
dotenc tools install-vscode-extension
dotenc tools install-agent-skill
dotenc tools install-agent-skill --forceinstall-vscode-extensionadds extension recommendations for supported editors and can open the extension page.install-agent-skillinstalls the dotenc agent skill throughnpx skills add.--forcemaps to non-interactive mode (-y) for automation.
Team Collaboration
In a real-world scenario, you will likely have multiple environments (e.g., development, test, production) and a team of developers who need access to these environments. Let's walk through how to set this up.
Granting access to a new team member
Alice just joined your team and she needs access to the shared environments, except production. She already has an SSH key (because of course she does - she's a developer).
She sends you her public key (~/.ssh/id_ed25519.pub) β it's a public key, so Slack, email, or even a sticky note will do β and you grant her access:
git checkout -b grant-alice-key
dotenc key add alice --from-file alice.pub
dotenc auth grant development alice
dotenc auth grant test alice
git add .
git commit -m "Grant alice access to development and test environments"
git pushNow, Alice will be able to decrypt the development and test environments using her SSH key. No new tools for her to install, no custom keys to generate - just her existing SSH key.
Revoking access from a team member
To fully offboard a team member (e.g., John), use auth purge:
dotenc auth purge john --yesThis revokes and re-encrypts every affected environment, then removes his public key file. Then, commit your changes:
git checkout -b offboard-john
git add .
git commit -m "Offboard John from all environments"
git push origin offboard-johnOnce merged, he will no longer be able to decrypt any environments.
If you only want to remove the key file without revoking environment access, use key remove:
dotenc key remove johnThis removes the .pub file only. Access to encrypted environments is left intact until you run auth purge.
Listing access
dotenc auth list [environment]Lists all public keys that have access to the specified environment.
Offboarding a Team Member
Use auth purge to fully offboard a team member in one step:
dotenc auth purge <user> [--yes]This command:
- Revokes the user's access from all environments they were granted
- Re-encrypts each environment without their key (data key rotation)
- Deletes their public key file from
.dotenc/
After running auth purge, also:
- Rotate external secrets (database passwords, API tokens, etc.)
- Deploy updated configuration
Individual environment management
dotenc env delete [environment] [--yes]Deletes an environment file in the current directory. In a monorepo, cd to the directory containing the file first.
dotenc env rotate [environment]Rotates the data key for a single environment in the current directory. In a monorepo, cd to the directory containing the file first.
dotenc env rotate --all [--yes]Rotates the data key for all environments in one step β recursively discovers and rotates every .env.*.enc file under the project tree. Useful for periodic key rotation or after a security event.
CI/CD Integration
CI runners and build servers need their own identity to decrypt environments. The approach is the same as local development: generate a key, grant access, and provide the private key at runtime.
1. Generate a dedicated CI key
Create an Ed25519 key pair for your CI environment. Do not set a passphrase:
ssh-keygen -t ed25519 -f ci_key -N "" -C "ci"This produces two files: ci_key (private) and ci_key.pub (public).
2. Add the key and grant access
Register the public key in the project and grant it access to the environments CI needs:
dotenc key add ci --from-ssh ./ci_key
dotenc auth grant test ci
dotenc auth grant production ci
git add .
git commit -m "Add CI key and grant access to test and production"
git push3. Set the private key in your CI provider
Copy the entire contents of the private key file and store it as a secret environment variable named DOTENC_PRIVATE_KEY in your CI provider (GitHub Actions, GitLab CI, CircleCI, etc.):
cat ci_key-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbm...
-----END OPENSSH PRIVATE KEY-----Paste the full output β including the BEGIN and END lines β as the value of DOTENC_PRIVATE_KEY.
If this private key is passphrase-protected, also set:
DOTENC_PRIVATE_KEY_PASSPHRASE=<your-passphrase>Once stored, delete the local private key file:
rm ci_keyThe public key (ci_key.pub) can also be deleted β it's already tracked inside .dotenc/ci.pub.
4. Use dotenc in your CI pipeline
With DOTENC_PRIVATE_KEY set (and DOTENC_PRIVATE_KEY_PASSPHRASE when using an encrypted key), dotenc will automatically pick up the key. No ~/.ssh directory required:
dotenc run -e test npm test
dotenc run -e production node app.jsGitHub Actions example
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
- run: npm ci
- run: npm install -g @dotenc/cli
- run: dotenc run -e test npm test
env:
DOTENC_PRIVATE_KEY: ${{ secrets.DOTENC_PRIVATE_KEY }}
DOTENC_PRIVATE_KEY_PASSPHRASE: ${{ secrets.DOTENC_PRIVATE_KEY_PASSPHRASE }}Key Management
dotenc keeps key management minimal by design. Your SSH keys are your identity - dotenc just uses them.
Private keys stay in
~/.ssh/where they belong. They are never copied or moved. Public keys are stored in your project's.dotenc/folder, derived from the corresponding private keys.
Supported Key Types
dotenc supports the following SSH key types:
- Ed25519
- RSA (2048-bit or larger)
These types are widely supported and provide strong security guarantees.
Note: dotenc can use passphrase-protected SSH keys when
DOTENC_PRIVATE_KEY_PASSPHRASEis set. In interactive flows (dotenc initand interactivedotenc key add), selecting a passphrase-protected key also offers an optional passwordless copy flow (for exampleid_ed25519_passwordless). If you prefer a dedicated passwordless key, you can generate one with:ssh-keygen -t ed25519 -N ""
Adding a public key
dotenc key add [name] [--from-ssh <path>] [-f, --from-file <file>] [-s, --from-string <pem_string>]Adds a public key into the project (.dotenc/<name>.pub).
--from-ssh <path>β Derive the public key from an SSH key file (private or public). Supports both Ed25519 and RSA keys.-f, --from-file <file>β Read a public (or private) key from a PEM file.-s, --from-string <pem_string>β Use a PEM string directly.- No arguments β Interactive mode: choose from your SSH keys or paste a PEM public key.
- Key names may contain letters, numbers, dots (
.), hyphens (-), and underscores (_).
Listing public keys
dotenc key listLists all public keys in the project, showing each key's name and algorithm.
Removing a public key
dotenc key remove [name]Removes the public key file from the project (.dotenc/<name>.pub). Does not revoke environment access. To fully offboard a key β revoking access from all environments and deleting the file β use auth purge instead.
Monorepo Usage
dotenc works in monorepos out of the box. Run dotenc init once at the repository root to create a shared .dotenc/ folder and a root-level environment. Subdirectory packages can then have their own .env.*.enc files that overlay the root.
How environment loading works:
dotenc run(anddev) walk from the project root to the current directory, loading and merging each layer. Local values override root values.- Use
--local-onlyto load only the current directory's environments, skipping ancestor layers.
Creating, editing, rotating, and deleting environments:
All env write commands operate on the current directory. cd to the target directory first:
# Create an environment in a package subdirectory
cd packages/web
dotenc env create staging
# Edit, rotate, or delete that same file
dotenc env edit staging
dotenc env rotate staging
dotenc env delete stagingListing environments:
dotenc env list # list environments in the current directory
dotenc env list --all # recursively list all environments in the projectBatch operations cover the whole tree:
dotenc env rotate --all --yes # rotate all .env.*.enc files recursively
dotenc auth purge alice --yes # revoke alice from all envs in the projectTips
For convenience, you can setup your package.json file like this:
// ...
"scripts": {
"dev": "dotenc dev tsx src/app.ts",
"start": "dotenc run -e production node dist/app.js",
"test": "dotenc run -e test vitest"
}Alternatively, the DOTENC_ENV variable can be used to set the environment, so the -e option can be omitted. For example:
export DOTENC_ENV="production"
dotenc run node app.jsUpdate checks
The CLI checks for new versions when you run dotenc dev and prints a notification when an update is available.
How dotenc compares
dotenc is a Git-native encryption layer designed for teams who want encrypted environment files committed alongside their code. It does not aim to replace centralized secret managers like Vault or Doppler β it serves a different operational model.
| Capability | dotenc | SOPS (+ age) | Vault | Doppler | |-------------|---------|--------------|--------|----------| | Git-native encrypted files | β | β | β | β | | Uses existing SSH identity | β | β (age / PGP) | β | β | | No external service required | β | β | β | β | | Environments versioned with code | β | β | β | β | | Centralized runtime secret API | β | β | β | β | | Dynamic / short-lived secrets | β | β | β | β | | Built-in org policy engine | β | β | β | β | | Requires running infrastructure | β | β | β | β |
When NOT to use dotenc
- If you require centralized policy enforcement
- If your compliance mandates HSM-backed key storage
- If you need runtime secret injection via remote API

