
29 Mar 2023
By Derick Olotu
Published on Recent
It usually started with a Slack DM.
A new developer, or worse, a product designer trying to spin up the local environment to test a UI tweak, would hit a wall. The app wouldn't boot. The inevitable message would follow: "Hey, does anyone have the latest staging .env file?" Someone would copy a massive block of sensitive keys, paste it into a private message, and hope for the best.
And then there was the ultimate nightmare: the accidental commit. Despite our best intentions, someone would inevitably type git commit -am 'quick fix' without double-checking their .gitignore, leaking a production API key right into the repository. The fallout was always agonizing—hours spent panicking, rotating credentials across multiple services, painfully rewriting Git history to scrub the commit, and praying we caught the leak before an automated bot did.
We knew this was a terrible experience. It was a massive productivity drain and an unacceptable security risk. We needed a better way to manage environment variables across our team, which led us to build RunEnv.
But building a secure vault was only half the battle. Integrating it seamlessly into the daily workflow of a product team turned out to be a massive UX and engineering challenge.
We didn't just want a centralized dashboard where people could copy-paste secrets slightly more securely. We wanted to eliminate the manual handling of secrets entirely.
Our target architecture relied on a modern stack: Next.js on the web, Upstash Redis for fast session management and rate limiting, and a desktop companion app built on Electron 37, React 19, and Vite. For security, everything had to be locked down with AES-256-GCM encryption via libsodium.
The mandate was simple in theory: Secrets should never touch the disk. But as we soon discovered, that "zero-disk" promise created a fascinating product paradox.
The core tension in designing RunEnv wasn't a fight over component libraries or color palettes. It was a philosophical debate between Design and Engineering: How invisible should security be?
If our system guarantees zero-disk storage and in-memory injection, the user technically never needs to see the secrets. But human psychology doesn't work that way. Users want to see their secrets. They want to verify they exist, check that they haven't been corrupted, and confirm they are looking at the right environment.
We went through three distinct iterations of the dashboard's secret viewer:
While Design wrestled with visibility, Engineering hit a wall with our editor integration.
To make RunEnv truly seamless, we built a VS Code extension. We wanted it to do everything: IntelliSense, diagnostics, local .env imports, and Flutter auto-detection. Predictably, our extension.ts file rapidly bloated into a 3,000+ line monolith. It became a nightmare to debug. We had to make the painful call to halt feature work for an entire sprint just to refactor it into 15+ focused modules (like workspace-context and doctor-report). It cost us a week, but it saved every sprint that followed.
Then came the authentication nightmare.
Our first implementation attempted to link OAuth accounts by matching email addresses. We quickly learned that developers rarely use the same email for GitHub and Google. When users tried to log in via a secondary provider, the app failed to match them. We attempted an "upsert-by-email" fix, but that inadvertently spawned duplicate ghost accounts instead of linking them.
After days of untangling the database, we ripped it out and implemented a much more resilient cookie-based session approach for account linking.
The real "aha!" moment came when we finalized how secrets actually made it into the developer's local environment.
Initially, we explored generating temporary, hidden .env files. But writing to disk—even temporarily—felt like a compromise. The breakthrough was realizing we didn't need files at all.
Using the VS Code environment API, we built an in-memory injection system. The extension fetches the encrypted payload, decrypts it locally, and injects the variables directly into every terminal, task, and debug session spawned by the editor. The secrets exist entirely in the active process memory. The moment you close the window, they are gone.
The resulting architecture is lean and highly secure:
Despite packing in 25 compiled modules after our massive refactor, the packaged VS Code extension weighs in at a mere 72 KB. By stripping out bundled dependencies and compiling TypeScript directly against the editor API, we kept the footprint nearly invisible.
Looking back, the biggest takeaway wasn't cryptographic; it was about empathy. Security tools often fail because they treat UX as an afterthought. By treating "invisible security" as a design problem, and by taking the time to refactor our monolithic extension early, we built a tool that developers actually want to use.
We finally put an end to the Slack .env DM and the dreaded Git history rewrite.
Want to stop passing secrets over Slack? You can try RunEnv for yourself right now. It is completely free to get started.

29 Mar 2023

14 Dec 2024
Sometimes, the best way to move forward is to take a moment to look around. On December 14th, the entire Jambo team traded our screens for the savanna.

19 Aug 2023