Insight

Envault

A small tool I built to stop leaving secrets in plaintext .env files. AWS credentials, API tokens, anything — encrypted at rest, injected into the subprocess on demand, gone when the process exits. Public, MIT-licensed, on GitHub today.

A vintage iron safe on a wooden floor next to an old iMac, rendered in deep teal duotone

I've been using aws-vault for some time. It does one thing well: keep your AWS credentials out of plaintext files on disk, and hand short-lived ones to whatever subprocess actually needs them. It's good software.

The trouble is that AWS credentials are never the only secrets on the machine. There's the Stripe key for the side project. The OpenAI token for the prototype. The database URL for the staging environment that isn't behind SSO yet. The webhook signing secret that one client emailed you. None of these are AWS, so aws-vault won't touch them — and they end up where secrets always end up when there's nowhere good to put them: in .env files lying around the filesystem in plaintext, getting accidentally committed, getting backed up to places they shouldn't be, sitting in ~/Projects/ long after the project is dead.

Some frameworks solve this for the framework. Rails has encrypted credentials and they work well — for Rails. The cases I kept hitting were the ones outside that boundary: an OpenTofu run that needs a provider token, a one-off Python script that talks to a vendor API, a Make target that wraps a psql against a managed database. The Rails app is fine. Everything around it is where the plaintext .env files breed.

I wanted the aws-vault posture for all of that. So I built one.

Envault is a small Go CLI that manages encrypted profiles of environment variables — AWS keys, API tokens, anything — and injects them into a subprocess on demand. It's public today, MIT licensed.

envault add stripe-prod STRIPE_SECRET_KEY=sk_live_...
envault exec stripe-prod -- ./run-migration.sh

The secrets live in an AES-256-GCM encrypted vault file (Argon2id for key derivation, OWASP parameters) or in the OS keyring — macOS Keychain, Linux libsecret — your choice. They never touch disk in plaintext. They exist in the child process's memory for as long as the child runs, and then they're gone.

A few things I wanted that aws-vault didn't give me, and that envault does:

  • Profile inheritance. A base profile with shared region and tags; a dev profile that inherits from it and overrides what it needs.
  • TTL on profiles. Mark a profile as expiring after 8 hours, or 7 days. If it's expired, exec refuses to run and tells you to refresh. Useful for credentials you rotate.
  • AWS role assumption still works. If a profile has AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_ROLE_ARN, envault calls STS AssumeRole before injecting — the subprocess gets short-lived credentials, the long-term keys never leave the vault.
  • Two backends. Encrypted file by default, keyring if you prefer the OS to hold the key.

It's small on purpose. It does what aws-vault does, plus the rest of the secrets you actually have. If that sounds useful:

go install github.com/Wa-Constellation/envault@latest

Source, issues, and the security policy are at github.com/Wa-Constellation/envault. Feedback welcome.


Thomas Riboulet is a Fractional VP of Engineering working with European tech companies. He writes about engineering leadership, team structure, and sustainable delivery at insights.wa-systems.eu.