Projects

Blogs

Careers

Contact

The End of .env in Slack: Building a Zero-Disk Secret Injector

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.


Understanding the Problem

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.


Design Exploration: The UX Paradox of Invisible Security

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:

  1. The Exposed Table: We initially showed all decrypted keys in a standard table. It felt incredibly unsafe, violating the very trust we were trying to build.
  2. The Masked View: We hid everything behind asterisks, requiring users to click "reveal" on every single row. It felt tedious and clunky.
  3. The Sweet Spot: We settled on a hybrid approach. Secrets are completely masked by default and only decrypted client-side on demand. We paired this with robust version history and visible audit logs, giving users confidence that their secrets were safe and up-to-date without requiring constant visibility.


Engineering Challenges: The Monolith and the OAuth Trap

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 Breakthrough: In-Memory Injection

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 Final Implementation

The resulting architecture is lean and highly secure:

  • Zero-Disk Workflow: No local files. Pure memory injection.
  • Hash-Only Token Storage: Service tokens are never stored in plaintext on our servers. Only their cryptographic hash lives in the database. The real token is shown to the user exactly once upon creation. A full database compromise would reveal zero usable credentials.
  • Automatic Refresh: Secrets operate on a configurable auto-refresh cycle. Combined with platform sync, a developer can update a secret in the RunEnv dashboard, and it is live in production within minutes—no redeployment required.



Results and Lessons Learned

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.

More Stories

Trusted Partners & Collaborators

Partner logo 1
Partner logo 2
Partner logo 3
Partner logo 4
Partner logo 5
Partner logo 6
Partner logo 7
Partner logo 8
Partner logo 9
Partner logo 10
Partner logo 11
Partner logo 12
Partner logo 13
Partner logo 14
Partner logo 1
Partner logo 2
Partner logo 3
Partner logo 4
Partner logo 5
Partner logo 6
Partner logo 7
Partner logo 8
Partner logo 9
Partner logo 10
Partner logo 11
Partner logo 12
Partner logo 13
Partner logo 14

Hope Together. Grow Together.
Building a world where everyone can thrive through technology and creativity.

Company

MissionProjectsCareersContact

Get in Touch

General Inquiry

contact@jambo.team

Partnerships

clients@jambo.team

© 2026 Seedbox LC. All rights reserved.

Crafted with

by Jambo.Team