DEV Community

Cover image for From AWS Key Leak to evnx: The Origin Story of a Developer's Safety Net
Ajit Kumar
Ajit Kumar

Posted on • Originally published at evnx.dev

From AWS Key Leak to evnx: The Origin Story of a Developer's Safety Net

How an AWS key leak, years of .env frustrations, and a poorly formatted LLM prompt accidentally built a developer tool.


Part 1: The Accident — An AWS Key Leak That Changed Everything

Every developer has that story. The one they hesitate to tell in interviews, wince at in retrospectives, and quietly reference whenever someone asks why security tooling matters. This is mine.

About a year ago, I was deep in a refactor of an Apache Airflow project at work — my first Airflow project, born from early-career learning and grown organically into a system with 10+ DAGs and a sprawling network of helper Python files. The project had been running for over a year. Scrapers, ETL tasks, one-off experiments that graduated to production — each added in its own folder, each with its own .env file.

At the end of a long day of refactoring, I just wanted to save my work. No ceremony. A quick git add ., a commit, and a push. The project didn't have pre-commit hooks at the time. Within minutes, things started breaking. Services went down. I went digging through backend code for bugs I'd introduced that day, and found nothing. Then I opened my email.

GitHub. AWS. Key revocation notices.

I had accidentally committed one of the nested .env files — one that contained AWS credentials and keys for several backend services. Everything connected to those keys was now broken. I had to explain the incident to my supervisor. I had to trace the blast radius. I had to rotate credentials and restore services one by one.

It was embarrassing. More than that, it was a moment that stuck with me at a deeper level than any code review comment ever had.

The immediate fix was pragmatic: I added pre-commit with secret scanning and moved on. But the underlying problem — the real one — quietly settled into the back of my mind, waiting.


Part 2: Years of .env Pain Across Projects

The AWS incident was acute, but the chronic pain had been building for years. Working across 7–8 projects at my previous job, plus personal and consulting work, I kept running into the same categories of friction around .env management — just in different flavors each time.

The .env vs .env.example drift problem. The idea is simple: commit a .env.example with all the keys but no real values, so other developers know what they need. In practice, as a project matures over months, keys get added to .env and never make it into .env.example. New team members clone the repo, fill out the example file, and then hit cryptic runtime errors because the example was out of date. What seems like a one-line discipline problem compounds into real onboarding pain.

Sharing and syncing across a distributed team. Software is a team sport, and every team member needs a working .env.local, .env.staging, or environment-specific variant. Add remote team members to the mix — especially part-timers with high turnover — and you have a constant cycle of sharing, revoking, rotating, and re-syncing credentials. For me, managing this for a team where people were frequently entering and leaving was one of the most draining non-coding tasks in the development workflow.

The wild west of .env formats. There's no formal specification for .env files. Every tool interprets them slightly differently. python-dotenv will happily parse quoted values like KEY="value" and strip the quotes for you. Docker's --env-file parser is stricter and will choke on the same syntax when building images. And then there are the migrations: for one project, I needed to move 30+ variables from a .env file into AWS Secrets Manager, which required a JSON format. Another project needed those same variables pushed into GitHub Actions secrets. Each migration meant careful, highly concentrated copy-paste — the kind where one missed variable or one formatting mistake could mean a broken deployment or a security gap. Nobody should be doing this manually.

All of it — the drift, the syncing, the format fragmentation — was death by a thousand cuts. Manageable, individually. Maddening, collectively.


Part 3: The Poorly Written Prompt That Started It All

Here's where the story takes an unexpected turn.

Sometime after the AWS incident, sitting with all of these accumulated frustrations about .env files, I did what many developers do: I opened an LLM and started typing. I wanted a comprehensive resource — a tutorial covering what .env files are, how to use them correctly, their limitations, and how different languages and ecosystems handle them.

My mistake: I forgot to specify the output format. I assumed it would come back as Markdown.

The LLM returned a beautifully structured HTML document.

I read through it. It was actually quite good. And in a moment of "well, it's just one file," I decided to host it. Bought a domain. Deployed the single HTML page. That became dotenv.space.

I added Google Analytics and Plausible, mostly out of curiosity. Within days, organic search traffic was arriving. People were landing on the page. The data made one thing clear: there was genuine demand for better .env tooling, not just documentation.

That weekend, I decided to dust off my Rust skills — about a year of on-and-off learning — and build a CLI that could actually solve the problems I had been living with. The first working version of what would become evnx came together in three to four days, with LLM assistance bridging the gaps in my Rust knowledge. I iterated on it, fixed bugs, used it on real projects, then did something that revealed the real state of the code: I read it line by line.

Even as a Rust beginner, I could see the issues. The structure wasn't right. The abstractions were leaking. So I started over — not from scratch, but from understanding. The second iteration was better. The third was something I felt good enough to ship.


Part 4: From dotenv-cli to evnx — A Name Born From Distribution

As the CLI matured through iterations, I turned my attention to distribution — a completely new problem space for me. I wanted the tool to be accessible everywhere developers already were: Cargo for Rust users, pip for Python users, npm for JavaScript developers, Homebrew for macOS, Scoop for Windows, winget for Windows package management, and GitHub Actions for CI/CD pipelines.

Each distribution channel has its own conventions, its own packaging requirements, its own review process. Working through all of them was an education in itself.

When it came to naming, I ran into a constraint: the name I originally had in mind — envX — was already taken across distribution channels. I needed something close, something that captured the spirit of the project.

The solution: evnx. Phonetically adjacent, practically distinct, and backronymed into something meaningful — Environment Variables Next Generation.

The name felt right. It acknowledged the lineage while staking out new territory.


Part 5: Where Things Stand Now

I'm currently in the gap between my last role and whatever comes next — a rare window of time that I've chosen to spend building evnx into something genuinely useful for other developers.

I use it myself. It solves the real problems that led to its creation: validating .env files, keeping .env.example in sync, scanning for accidentally committed secrets, converting between formats (JSON, YAML, Kubernetes, Docker, GitHub Actions, and more), and integrating into CI/CD pipelines via GitHub Actions.

The origin wasn't glamorous. It started with embarrassment, accumulated through years of low-grade frustration, and got catalyzed by a formatting oversight in an LLM prompt. But that's how a lot of useful tools get built — not from grand vision, but from lived pain that finally crosses the threshold of tolerance.

If any of this sounds familiar, I'd love for you to try evnx, file issues, suggest features, and help shape what it becomes.


Get Started with evnx

🌐 Official Website

evnx.dev — Documentation, installation guides, and full feature reference. Start here if you're new to evnx.

📦 Main Repository

github.com/urwithajit9/evnx — The core CLI source code. Open issues, submit pull requests, and follow development here.

Status:
✅ CI Passing
📦 Latest Release
🦀 crates.io
🐍 PyPI
📦 npm
📄 MIT License
🔧 GitHub Action

🧪 Test Repository

github.com/urwithajit9/evnx-test — Sample .env files, test fixtures, and integration test cases. Useful if you want to explore evnx commands against realistic inputs before running it on your own project.

⚙️ GitHub Actions Integration

github.com/urwithajit9/evnx-action — The official evnx GitHub Action. Add .env validation and secret scanning directly to your CI/CD pipeline with a few lines of YAML. Prevent leaks before they reach your repository.

🍺 Homebrew Tap (macOS / Linux)

github.com/urwithajit9/homebrew-evnx — Install evnx on macOS or Linux via Homebrew:

brew tap urwithajit9/evnx
brew install evnx
Enter fullscreen mode Exit fullscreen mode

🪟 Scoop Bucket (Windows)

github.com/urwithajit9/scoop-evnx — Install evnx on Windows via Scoop:

scoop bucket add evnx https://github.com/urwithajit9/scoop-evnx
scoop install evnx
Enter fullscreen mode Exit fullscreen mode

evnx is open source under the MIT License. Contributions, feedback, and stars are all deeply appreciated.

Top comments (0)