Backend
We Replaced the Entire Secrets Management Stack with Go's Standard Library
Vishnu Nandan Dev.to (EN Zone)
1 views
Somewhere right now, a developer is running npm install on an ordinary, legitimate-looking package.
The installation finishes with crisp green checkmarks. But buried three layers deep into that dependency tree, an innocuous postinstall script runs.
It does not need an unpatched zero-day.
It does not need kernel-level privilege escalation.
It does something much simpler:
It crawls up to your project root and quietly reads your .env file.
Plaintext database connection strings, Stripe live keys, and AWS access tokens get read directly off the disk and shipped across the wire to a remote server.
The developer did not click a phishing link.
They did not commit secrets to GitHub.
They followed every best practice in the book.
Upstream was already poisoned, and nobody knew.
The uncomfortable reality of modern development is a bizarre contradiction: we build digital fortresses in the cloud, then tape the root passwords to our monitors in a file called .env.
We entered the Hackathon Raptor's Zero Dependency Hackathon with a single mandate:
Build a real tool in 72 hours using nothing but the raw language standard library.
No frameworks.
No shortcuts.
Zero dependencies.
Most security tools try to build taller walls around exposed keys on a hard drive.
We decided to make them exist only in volatile memory; living and dying like mayflies.
Meet MayFly.
Prologue: The King of the Jungle (L.I.O.N)
MayFly didn’t pop into our heads randomly, and it definitely wasn't an idea spat out by some LLM.
Its seeds were planted during our previous hackathon. At that time, supply chain attacks were dominating every headline, and we wanted to build our own answer to the crisis; and we did: L.I.O.N.
L.I.O.N is a security sandbox written in Rust, designed to isolate package managers like npm, cargo, and pip inside restricted Linux namespaces before they can touch the real machine (essentially a wrapper around bubblewrap).
Whether looking at the Shai-Hulud worm or supply chain hits across npm, PyPI, and LiteLLM, the playbook was always the exact same. Malicious packages didn't bother with zero-day exploits or breaking out of virtualization. They simply looked for a .env file, scooped up the plaintext API keys, and shipped them to a remote server.
L.I.O.N solved this during package installation:
Wrap your package manager with it.
It runs inside a synthetic root where environment variables are wiped with --clearenv.
Sensitive dotfiles are never mounted. To any rogue script running inside the sandbox, .env files are completely invisible.
Essentially Deny by Invisibility. Powerful, yet it had its own friction during daily development.
A sandbox only protects the specific command you explicitly remember to wrap. The rest of the time, that .env file is still sitting on your hard drive in plaintext. The moment you run npm install without a sandbox, or the moment a teammate opens the repository on macOS or Windows (where Linux bubblewrap namespaces don't exist), the keys are exposed again.
Sandboxing spends all its energy building a fortress around a key left under the doormat.
We eventually flipped the problem on its head: take the key out from under the doormat, and you will never need the fortress.
You Can't Steal What Doesn't Exist
The mental model for MayFly is intentionally straightforward:
mayfly npm run dev
Instead of scattering plaintext .env files across your hard drive, your secrets live inside a single host-level encrypted vault.
When you execute your project through MayFly:
It inspects your active working directory to map it to the corresponding project workspace.
It switches the terminal into raw mode to capture your master passphrase without terminal echo.
It decrypts only that project's keys directly into volatile system memory.
It forks your dev command using Go's native process runner, injecting the secrets straight into the child process's in-memory environment table.
It records an entry in an immutable, hash-chained audit ledger.
The moment the child process exits, the private memory pages are reclaimed by the operating system.
Your application code continues to read process.env.DATABASE_URL exactly as it always did. Your codebase requires zero changes.
But if an unvetted package or a rogue script crawls the repository folder, there is literally nothing on disk to find.
It also gave us an unexpected realization: having zero dependencies meant MayFly had zero external attack surface. The very tool built to protect you from supply chain poison was immune to it.
(*to an extent)
Chapter 1: The Face Before the Brain
If you inspect MayFly's git history, the very first commits don't touch cryptography, process runners, or secret vaults.
We started with something most developers consider impossible under zero-dependency constraints: building an entire Terminal User Interface (TUI) engine from scratch.
In modern Go development, if you want an interactive terminal dashboard, the default answer is simple: import charmbracelet/bubbletea. Under zero-dependency rules, importing a popular TUI framework was an immediate disqualification.
Before we wrote a single cryptographic line, we spent the first phase building pkg/tui entirely out of Go standard library primitives:
Double-Buffered 2D Canvas: To eliminate the ugly screen flickering of naive terminal printing, we built an in-memory 2D cell grid (bytes.Buffer) that diffs old and new frames, writing only modified terminal cells to stdout.
ANSI Key Parser: We wrote a streaming finite-state machine that parses incoming raw bytes from os.Stdin into discrete arrow keys, Escape, Tab, and multi-byte UTF-8 runes in real time.
Unicode East Asian Width: Displaying emoji or wide characters breaks terminal column alignment because they take up two character cells. Without packages like go-runewidth, we implemented an East Asian width calculator by hand so the project grid never distorts.
Zero-History Ephemeral Screens: When you inspect secrets or edit a key (mf set or mf get), MayFly flips to the terminal alternate screen buffer (\x1b[?1049h). When you exit, the screen flips back and vanishes, leaving zero lines in your terminal scrollback history.
OSC 52 Clipboard Copying: To copy secrets to the system clipboard without pulling in external tools like xclip, wl-copy, or pbcopy, MayFly emits raw ANSI OSC 52 escape sequences (\x1b]52;c;...) straight to stdout.
We built the entire user experience first. Now, we had to build the engine beneath it.
Chapter 2: The First Challenge
With the interface alive, we needed a place to store secrets.
The architecture required an encrypted host-level vault (~/.mayfly/vault.enc) locked with AES-256-GCM. But AES requires a 256-bit cryptographic key, and users input human passphrases. To turn a passphrase into an encryption key resistant to GPU brute-forcing, you need a key derivation function: PBKDF2.
In routine Go development, this is a one-line import: golang.org/x/crypto/pbkdf2.
Under the hackathon's Zero Dependency rules, packages under golang.org/x/ are external dependencies. Even though they live under the official Go GitHub organization, they are maintained outside the core standard library distribution. Using one was an immediate disqualification.
While Go's standard library provides robust implementations of AES, GCM, and SHA-256, standard key stretching algorithms like PBKDF2 or Argon2 are completely missing.
We didn't want to compromise on security, so we decided to rebuild PBKDF2 ourselves using only the standard library's crypto/hmac and crypto/sha256. We leaned on AI to help reconstruct RFC 8018 (PKCS #5 v2.0) from the ground up, keeping our dependency manifest completely empty.
Rolling your own crypto comes with a catch: if you mess up even a tiny detail, like whether a block counter starts at 0 or 1, your encryption silently fails. To make sure our implementation was genuinely bulletproof, we elevated our work factor to 600,000 iterations (the OWASP standard) and validated every derived key against standard OpenSSL test vectors before plugging it into our AES-GCM vault pipeline.
Chapter 3: Paths Lie. Inodes Don't.
Now that we had an encrypted vault, we hit a subtle architectural problem: when a developer runs MayFly in a directory, how does the tool know which secrets belong to that project?
The obvious, naive approach is using directory path strings: /home/dev/projects/my-api.
Path strings are fragile, and in security, fragility is a liability. If a developer renames a folder or clones a repo into a different path, the association breaks. Even worse, an attacker or rogue script could abuse symlinks to fool a tool into resolving the wrong directory and injecting production secrets into an untrusted environment.
We decided to skip path strings entirely.
Instead, MayFly anchors projects directly to the disk's physical filesystem geometry:
It resolves the canonical path through filepath.EvalSymlinks, stripping away any symlink tricks.
It queries the operating system kernel for the physical device and inode:
On Linux and macOS, it reads the physical Device ID and Inode number using syscall.Stat_t.
On Windows, it extracts the unique FileIndex via GetFileInformationByHandle.
Your project isn't identified by an arbitrary folder name. It is tied to the exact physical sectors on your drive. You can rename the folder, move its parent, or create aliases: MayFly doesn't care about names. It only cares about the physical inode.
Chapter 4: The Forgotten Magic of Process Spawning
With identity locked down and secrets safely encrypted, we arrived at MayFly's core promise: making .env files obsolete.
We have been conditioned to install packages for tasks operating systems solved decades ago. To load local config, we instinctively reach for dotenv or run background daemons listening on local sockets. We eliminated both by leaning directly on Go's built-in os/exec.
Whenever an operating system spawns a child process, the parent constructs an environment table in memory and hands it directly to the child during process creation. In Go, this relies on a fundamental standard library primitive:
cmd := exec.Command(args[0], args[1:]...)
// Inject decrypted secrets directly into the child process memory table:
cmd.Env = append(os.Environ(), decryptedSecrets...)
cmd.Run()
When your Node.js, Python, or Go server boots up, process.env.DATABASE_URL is already waiting in RAM.
No files on disk.
No background services polling network sockets.
Zero runtime packages added to your package.json or go.mod.
The operating system kernel manages the environment block, and your application never even knows MayFly was involved.
Chapter 5: The DX Pivot (Killing Syntax Friction)
If you look at the commit log mid-way through the hackathon, our initial execution command looked like this:
mayfly run -- npm run dev
It worked, but it felt clunky. The double hyphen (--) is standard Unix convention for separating flags from positional arguments, but during day-to-day development, developers hate unnecessary syntax.
Commit b4a91f9 marks a critical pivot in MayFly's design: transparent command interception.
We refactored the CLI argument parser so that MayFly inspects the first argument: if it matches a known subcommand (init, set, get, scan, audit), it runs internal management routines. But if it does not match an internal command, MayFly immediately assumes you want to execute code:
# Before:
mayfly run -- npm run dev
# After:
mayfly npm run dev
# Or with the 2-letter alias:
mf npm run dev
By removing the syntax hurdle, MayFly stopped feeling like a cumbersome security wrapper and started feeling like a native system tool.
Chapter 6: The Cross-Platform Trenches
Everything ran cleanly on Linux and macOS. Then our team member Alvi started running the test suite on Windows.
What followed was a marathon of low-level edge cases that you never encounter until you strip away external helper libraries:
Windows Raw Mode: Unix relies on POSIX ioctl calls with TCGETS/TCSETS to clear the terminal echo flag. Windows consoles do not implement POSIX termios. We had to interact directly with kernel32.dll using Go's syscall package to manipulate console modes by hand: clearing ENABLE_ECHO_INPUT while enabling ENABLE_VIRTUAL_TERMINAL_INPUT.
Terminal Interrupt Signals: If a user pressed Ctrl+C while the terminal was in raw input mode, the console remained permanently broken after exit: keystrokes became invisible and line wrapping failed. We had to wire up signal listeners via Go's os/signal to intercept interrupts and guarantee original console modes were restored.
CRLF Line Endings: Windows terminal password prompts appended \r\n instead of \n, silently injecting carriage returns into master passwords and causing cross-platform decryption failures.
The PowerShell UTF-8 BOM Trap: This was the most insidious bug of the entire weekend. On Windows, PowerShell commands often output text files with a hidden 3-byte UTF-8 Byte Order Mark (\xef\xbb\xbf). When MayFly loaded project metadata and audit logs, the invisible BOM broke JSON unmarshaling and caused SHA-256 hash checks to fail. We had to implement custom BOM-stripping readers across our file I/O layer.
Fighting these edge cases without third-party abstraction packages was exhausting, but it forced us to understand how operating systems actually behave under the hood.
Chapter 7: If You Touch It, the Math Breaks
A tool that quietly injects credentials needs verifiable transparency. If someone accesses a secret or updates a vault entry, there has to be a permanent, tamper-proof record.
Instead of bundling an embedded database engine, we built a cryptographic audit log directly on top of standard file I/O and crypto/sha256.
Every time MayFly decrypts, injects, or updates a key, it appends a structured entry to a local ledger (~/.mayfly/audit.log). Each entry calculates its own digest by combining its metadata with the hash of the entry before it:
$$\text{Block Hash}n = \text{SHA256}(\text{Block Hash}{n-1} + \text{Timestamp} + \text{Operation} + \text{ProjectID})$$
If an attacker tries to delete a line, edit a past timestamp, or cover their tracks, every subsequent hash in the chain breaks. Running mf audit verify instantly flags the exact record that was manipulated.
We applied this exact same paranoia to our installer and in-place self-updater (mf update).
Rather than blindly executing remote scripts, both the shell installer and the Go binary stream cryptographic checksums (checksums.txt) directly from GitHub releases. The binary hashes itself, verifies its own integrity against the published digest, and performs an atomic in-place swap only if the math matches.
From the moment you download the binary to the moment it injects a secret into RAM, trust isn't assumed: it's always verified with math.
Chapter 8: Completing the Arsenal
In the final hours before submission, we rounded out MayFly to handle the entire developer security lifecycle without needing external tools:
The Built-in Leak Scanner & Git Hook (mf scan): Instead of pulling in gitleaks or trufflehog, we wrote a static analysis scanner using Go's filepath.WalkDir, bufio.Scanner, and compiled regex patterns. It catches exposed AWS keys, OpenAI tokens, connection strings, and Dockerfile secrets before they get committed. Running mf install-hook installs a pre-commit hook directly into .git/hooks/ in one second.
The 1-Second .env Shredder (mf import --delete): Migrating to MayFly shouldn't be tedious. Running mf import ingests an existing .env directly into the encrypted vault, and the --delete flag securely shreds the plaintext file off your disk immediately.
Intermediate Memory Zeroing: We added explicit memory zeroization routines (pkg/executor and pkg/vault) so that intermediate cryptographic keys and decrypted secrets are wiped from RAM arrays the moment operations finish.
Encrypted Disaster Recovery (mf backup): Because secrets never touch disk in plaintext, teams still need safe ways to migrate or backup their vaults. Running mf backup exports a password-protected, encrypted snapshot of your projects and keys that you can safely restore anywhere.
Epilogue: What 72 Hours of Zero Dependencies Taught Us
Building without external dependencies shifts your entire development perspective.
The current default in software engineering is to treat external packages as elementary building blocks. Need a password prompt? Install a package. Need an RFC implementation? Install a package. Need a TUI? Install a framework. Need to load configuration? Install another package.
Every dependency pulled into a project is code written by someone else, running with your permissions, and granted access to your machine.
By the time the submission window closed:
Our go.mod file contained zero external requirements.
The binary compiled down to a single self-contained executable for Linux, macOS, and Windows.
Plaintext credentials were completely eliminated from project directories.
It also gave us an unexpected realization: having zero dependencies meant MayFly had zero external attack surface. The very tool built to protect you from supply chain poison was immune to it.
You do not always need a massive dependency tree to build secure, cross-platform developer tools. Understanding the standard library and the operating system primitives beneath it is often all the foundation you actually need.
MayFly GitHub: vishnunandan555/mayfly
MayFly Documentation: mayfly-docs.vercel.app
L.I.O.N GitHub: A56-A5/lion
Built by vishnunandan555, A56-A5, and WanderingHumanid for the Hackathon Raptors Zero Dependency Hackathon.
Read original: https://dev.to/vishnunandan555/we-replaced-the-entire-secrets-management-stack-with-gos-standard-library-b13
← Previous
300+ Iterations Later: My Two-Year Journey Designing a New Reactive Paradigm
Next →
IonQ Analysis Shows 20,000 Qubits Can Crack Bitcoin Security
Related
Zero Dependencies Sounds Easy Until You Have to Build Everything Yourself
Backend
2
DEV Community
Zero Dependencies, 456 Tests, and One Bug All of Them Missed
Backend
4
DEV Community
7 Ways to Make Your API Faster
Backend
1
DEV Community
Laravel 13: A Practical Guide for PHP Developers
Backend
2
Dev.to (EN Zone)
Comments0
No comments yet — be the first