You know the freeze. git push, then half a second later: wait — is .env actually in .gitignore?
The .env file is how most of us handle secrets locally, and it's a mess. Plaintext. Sits on laptops forever. Gets pasted into Slack. Occasionally rides along into a public commit. Even teams paying for a proper secrets manager tend to do the same local dance: open the vault UI, copy the token, paste it into a .env, pray. I got tired of that, so I tried to get rid of the file entirely.
Here's the thing that bugs me about the status quo: production and local are worlds apart. Vault or a cloud SDK on a server? Fine, reasonable. But making someone run a daemon or write SDK boilerplate just to read a DB string on their own laptop is friction, and people route around friction every time. That's how plaintext creds end up on a dozen machines in the first place.
So I gave myself three rules: nothing plaintext on disk, no changes to app code (it has to work with Node, Python, Go, Rust, whatever), and no daemon running in the background.
env-pull is a small Go CLI. Rather than write secrets to a file, it wraps your command, pulls the secrets at runtime, and hands them straight to the child process's environment. Here's the path when it's pulling from AWS Secrets Manager:
env-pull run --aws-secret prod/db -- psql mydb
│
├─ 1. Load AWS default credential chain (zero new config)
├─ 2. Call secretsmanager:GetSecretValue("prod/db")
├─ 3. Parse JSON payload → map[string]string in memory
├─ 4. Merge with current environment (no overwrites)
├─ 5. exec(psql, env=[...existing..., DB_PASSWORD=s3cr3t, ...])
└─ 6. psql exits → memory is reclaimed, secrets are gone
It's plain os/exec underneath, so your app just sees normal environment variables — no idea anything injected them, no code to change. The moment the process exits, the OS takes the memory back and the secrets go with it. Nothing to clean up.
Here's a throwaway Node script that just prints what it finds in the environment:
// index.js
console.log("=== Application Lifecycle Initialized ===");
console.log("Database Access String : ", process.env.DATABASE_URL || "[MISSING - Connection Failed]");
console.log("Payment Gateway Token : ", process.env.STRIPE_SECRET_KEY || "[MISSING - Fallback Triggered]");
console.log("=== Application Lifecycle Terminated ===");
Run it the normal way and there's nothing there:
$ node index.js
=== Application Lifecycle Initialized ===
Database Access String : [MISSING - Connection Failed]
Payment Gateway Token : [MISSING - Fallback Triggered]
=== Application Lifecycle Terminated ===
If you're not on a cloud provider, there's an offline path. env-pull edit opens your default editor:
$ env-pull edit
You type variables like a normal .env:
DATABASE_URL=postgres://local_dev_user:crypt_pass_99@localhost:5432/dev_db
STRIPE_SECRET_KEY=sk_test_51NxLocalOverrideKey
On save, env-pull grabs the buffer, overwrites the temp file with zeros, and encrypts the payload with AES-256-GCM. What's on disk is ciphertext and nothing else:
$ cat .env.pull.enc
U2FsdGVkX19vPlu8Y1bqm1h3S6Kx8vW9ZJ2H0m9L7qX+P... [Encrypted Binary Stream]
To run, prefix your command:
$ env-pull run -- node index.js
=== Application Lifecycle Initialized ===
Database Access String : postgres://local_dev_user:crypt_pass_99@localhost:5432/dev_db
Payment Gateway Token : sk_test_51NxLocalOverrideKey
=== Application Lifecycle Terminated ===
Launch the app the normal way right afterward and the values are already gone — not on disk, not in your shell history:
$ node index.js
=== Application Lifecycle Initialized ===
Database Access String : [MISSING - Connection Failed]
=== Application Lifecycle Terminated ===
Now the honest part, because there's no silver bullet here. env-pull is built for software-layer leaks: accidental commits, screen shares, plaintext sitting at rest on an SSD. It is not a hardware guarantee.
Zeroing buffers in a GC'd language like Go is best-effort, full stop. On copy-on-write or log-structured filesystems — APFS, ZFS, most NVMe SSDs — the OS shuffles physical blocks around on its own, so if you want a true physical wipe you need block-level disk encryption underneath. And anyone with root can read /proc/<pid>/environ or dump memory; if your machine is fully owned, this won't save you. What it does kill is the boring, everyday leak that actually takes teams down.
I built this for my own workflow, but I'm wiring in more upstream integrations: GCP Secret Manager, Azure Key Vault, 1Password CLI, and Bitwarden CLI are in progress.
It's open source, and I'd really like people poking at it — read the code, try the install, tell me what I got wrong. Repo's here. If a Homebrew or Scoop install chokes, open an issue.
Harsh Sonkar
Software Engineer
Repo: https://github.com/dynamicHarsh/env-pull