<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vishnu Nandan</title>
    <description>The latest articles on DEV Community by Vishnu Nandan (@vishnunandan555).</description>
    <link>https://dev.to/vishnunandan555</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3925275%2F040ccc2a-3ffa-4f94-9e97-1ef81a45dae0.jpg</url>
      <title>DEV Community: Vishnu Nandan</title>
      <link>https://dev.to/vishnunandan555</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vishnunandan555"/>
    <language>en</language>
    <item>
      <title>We Replaced the Entire Secrets Management Stack with Go's Standard Library</title>
      <dc:creator>Vishnu Nandan</dc:creator>
      <pubDate>Tue, 08 Sep 2026 17:30:06 +0000</pubDate>
      <link>https://dev.to/vishnunandan555/we-replaced-the-entire-secrets-management-stack-with-gos-standard-library-b13</link>
      <guid>https://dev.to/vishnunandan555/we-replaced-the-entire-secrets-management-stack-with-gos-standard-library-b13</guid>
      <description>&lt;p&gt;Somewhere right now, a developer is running &lt;code&gt;npm install&lt;/code&gt; on an ordinary, legitimate-looking package.&lt;/p&gt;

&lt;p&gt;The installation finishes with crisp green checkmarks. But buried three layers deep into that dependency tree, an innocuous &lt;code&gt;postinstall&lt;/code&gt; script runs. &lt;/p&gt;

&lt;p&gt;It does not need an unpatched zero-day. &lt;br&gt;
It does not need kernel-level privilege escalation. &lt;br&gt;
It does something much simpler: &lt;br&gt;
It crawls up to your project root and quietly reads your &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;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. &lt;/p&gt;

&lt;p&gt;The developer did not click a phishing link. &lt;br&gt;
They did not commit secrets to GitHub. &lt;br&gt;
They followed every best practice in the book.&lt;br&gt;
Upstream was already poisoned, and nobody knew.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We entered the &lt;strong&gt;Hackathon Raptor's Zero Dependency Hackathon&lt;/strong&gt; with a single mandate: &lt;/p&gt;

&lt;p&gt;Build a real tool in 72 hours using nothing but the raw language standard library. &lt;br&gt;
No frameworks. &lt;br&gt;
No shortcuts.&lt;br&gt;
Zero dependencies.&lt;/p&gt;

&lt;p&gt;Most security tools try to build taller walls around exposed keys on a hard drive.&lt;/p&gt;

&lt;p&gt;We decided to make them exist only in volatile memory; living and dying like mayflies.&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;&lt;a href="https://github.com/vishnunandan555/mayfly" rel="noopener noreferrer"&gt;MayFly&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Prologue: The King of the Jungle (L.I.O.N)
&lt;/h3&gt;

&lt;p&gt;MayFly didn’t pop into our heads randomly, and it definitely wasn't an idea spat out by some LLM. &lt;/p&gt;

&lt;p&gt;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: &lt;strong&gt;&lt;a href="https://github.com/A56-A5/lion" rel="noopener noreferrer"&gt;L.I.O.N&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;L.I.O.N is a security sandbox written in Rust, designed to isolate package managers like &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;cargo&lt;/code&gt;, and &lt;code&gt;pip&lt;/code&gt; inside restricted Linux namespaces before they can touch the real machine (essentially a wrapper around &lt;code&gt;bubblewrap&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;.env&lt;/code&gt; file, scooped up the plaintext API keys, and shipped them to a remote server.&lt;/p&gt;

&lt;p&gt;L.I.O.N solved this during package installation: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Wrap your package manager with it.&lt;/li&gt;
&lt;li&gt;It runs inside a synthetic root where environment variables are wiped with &lt;code&gt;--clearenv&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Sensitive dotfiles are never mounted. To any rogue script running inside the sandbox, &lt;code&gt;.env&lt;/code&gt; files are completely invisible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Essentially Deny by Invisibility. Powerful, yet it had its own friction during daily development. &lt;/p&gt;

&lt;p&gt;A sandbox only protects the specific command you explicitly remember to wrap. The rest of the time, that &lt;code&gt;.env&lt;/code&gt; file is still sitting on your hard drive in plaintext. The moment you run &lt;code&gt;npm install&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Sandboxing spends all its energy building a fortress around a key left under the doormat. &lt;/p&gt;

&lt;p&gt;We eventually flipped the problem on its head: &lt;strong&gt;take the key out from under the doormat, and you will never need the fortress.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  You Can't Steal What Doesn't Exist
&lt;/h3&gt;

&lt;p&gt;The mental model for MayFly is intentionally straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mayfly npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of scattering plaintext &lt;code&gt;.env&lt;/code&gt; files across your hard drive, your secrets live inside a single host-level encrypted vault.&lt;/p&gt;

&lt;p&gt;When you execute your project through MayFly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It inspects your active working directory to map it to the corresponding project workspace.&lt;/li&gt;
&lt;li&gt;It switches the terminal into raw mode to capture your master passphrase without terminal echo.&lt;/li&gt;
&lt;li&gt;It decrypts only that project's keys directly into volatile system memory.&lt;/li&gt;
&lt;li&gt;It forks your dev command using Go's native process runner, injecting the secrets straight into the child process's in-memory environment table.&lt;/li&gt;
&lt;li&gt;It records an entry in an immutable, hash-chained audit ledger.&lt;/li&gt;
&lt;li&gt;The moment the child process exits, the private memory pages are reclaimed by the operating system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your application code continues to read &lt;code&gt;process.env.DATABASE_URL&lt;/code&gt; exactly as it always did. Your codebase requires zero changes. &lt;/p&gt;

&lt;p&gt;But if an unvetted package or a rogue script crawls the repository folder, there is literally nothing on disk to find.&lt;/p&gt;

&lt;p&gt;It also gave us an unexpected realization: having zero dependencies meant MayFly had zero external attack surface. &lt;strong&gt;The very tool built to protect you from supply chain poison was immune to it.&lt;/strong&gt;&lt;br&gt;
(*to an extent)&lt;/p&gt;


&lt;h3&gt;
  
  
  Chapter 1: The Face Before the Brain
&lt;/h3&gt;

&lt;p&gt;If you inspect MayFly's git history, the very first commits don't touch cryptography, process runners, or secret vaults. &lt;/p&gt;

&lt;p&gt;We started with something most developers consider impossible under zero-dependency constraints: building an entire Terminal User Interface (TUI) engine from scratch.&lt;/p&gt;

&lt;p&gt;In modern Go development, if you want an interactive terminal dashboard, the default answer is simple: import &lt;code&gt;charmbracelet/bubbletea&lt;/code&gt;. Under zero-dependency rules, importing a popular TUI framework was an immediate disqualification. &lt;/p&gt;

&lt;p&gt;Before we wrote a single cryptographic line, we spent the first phase building &lt;code&gt;pkg/tui&lt;/code&gt; entirely out of Go standard library primitives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Double-Buffered 2D Canvas:&lt;/strong&gt; To eliminate the ugly screen flickering of naive terminal printing, we built an in-memory 2D cell grid (&lt;code&gt;bytes.Buffer&lt;/code&gt;) that diffs old and new frames, writing only modified terminal cells to stdout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ANSI Key Parser:&lt;/strong&gt; We wrote a streaming finite-state machine that parses incoming raw bytes from &lt;code&gt;os.Stdin&lt;/code&gt; into discrete arrow keys, Escape, Tab, and multi-byte UTF-8 runes in real time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unicode East Asian Width:&lt;/strong&gt; Displaying emoji or wide characters breaks terminal column alignment because they take up two character cells. Without packages like &lt;code&gt;go-runewidth&lt;/code&gt;, we implemented an East Asian width calculator by hand so the project grid never distorts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-History Ephemeral Screens:&lt;/strong&gt; When you inspect secrets or edit a key (&lt;code&gt;mf set&lt;/code&gt; or &lt;code&gt;mf get&lt;/code&gt;), MayFly flips to the terminal alternate screen buffer (&lt;code&gt;\x1b[?1049h&lt;/code&gt;). When you exit, the screen flips back and vanishes, leaving zero lines in your terminal scrollback history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OSC 52 Clipboard Copying:&lt;/strong&gt; To copy secrets to the system clipboard without pulling in external tools like &lt;code&gt;xclip&lt;/code&gt;, &lt;code&gt;wl-copy&lt;/code&gt;, or &lt;code&gt;pbcopy&lt;/code&gt;, MayFly emits raw ANSI OSC 52 escape sequences (&lt;code&gt;\x1b]52;c;...&lt;/code&gt;) straight to stdout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We built the entire user experience first. Now, we had to build the engine beneath it.&lt;/p&gt;


&lt;h3&gt;
  
  
  Chapter 2: The First Challenge
&lt;/h3&gt;

&lt;p&gt;With the interface alive, we needed a place to store secrets. &lt;/p&gt;

&lt;p&gt;The architecture required an encrypted host-level vault (&lt;code&gt;~/.mayfly/vault.enc&lt;/code&gt;) 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.&lt;/p&gt;

&lt;p&gt;In routine Go development, this is a one-line import: &lt;code&gt;golang.org/x/crypto/pbkdf2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Under the hackathon's Zero Dependency rules, packages under &lt;code&gt;golang.org/x/&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;We didn't want to compromise on security, so we decided to rebuild PBKDF2 ourselves using only the standard library's &lt;code&gt;crypto/hmac&lt;/code&gt; and &lt;code&gt;crypto/sha256&lt;/code&gt;. We leaned on AI to help reconstruct RFC 8018 (PKCS #5 v2.0) from the ground up, keeping our dependency manifest completely empty.&lt;/p&gt;

&lt;p&gt;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 &lt;strong&gt;600,000 iterations&lt;/strong&gt; (the OWASP standard) and validated every derived key against standard OpenSSL test vectors before plugging it into our AES-GCM vault pipeline.&lt;/p&gt;


&lt;h3&gt;
  
  
  Chapter 3: Paths Lie. Inodes Don't.
&lt;/h3&gt;

&lt;p&gt;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?&lt;/p&gt;

&lt;p&gt;The obvious, naive approach is using directory path strings: &lt;code&gt;/home/dev/projects/my-api&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;We decided to skip path strings entirely. &lt;/p&gt;

&lt;p&gt;Instead, MayFly anchors projects directly to the disk's physical filesystem geometry:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It resolves the canonical path through &lt;code&gt;filepath.EvalSymlinks&lt;/code&gt;, stripping away any symlink tricks.&lt;/li&gt;
&lt;li&gt;It queries the operating system kernel for the physical device and inode:

&lt;ul&gt;
&lt;li&gt;On Linux and macOS, it reads the physical &lt;code&gt;Device ID&lt;/code&gt; and &lt;code&gt;Inode number&lt;/code&gt; using &lt;code&gt;syscall.Stat_t&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On Windows, it extracts the unique &lt;code&gt;FileIndex&lt;/code&gt; via &lt;code&gt;GetFileInformationByHandle&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;


&lt;h3&gt;
  
  
  Chapter 4: The Forgotten Magic of Process Spawning
&lt;/h3&gt;

&lt;p&gt;With identity locked down and secrets safely encrypted, we arrived at MayFly's core promise: &lt;strong&gt;making &lt;code&gt;.env&lt;/code&gt; files obsolete.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have been conditioned to install packages for tasks operating systems solved decades ago. To load local config, we instinctively reach for &lt;code&gt;dotenv&lt;/code&gt; or run background daemons listening on local sockets. We eliminated both by leaning directly on Go's built-in &lt;code&gt;os/exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;// Inject decrypted secrets directly into the child process memory table:&lt;/span&gt;
&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environ&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;decryptedSecrets&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your Node.js, Python, or Go server boots up, &lt;code&gt;process.env.DATABASE_URL&lt;/code&gt; is already waiting in RAM. &lt;/p&gt;

&lt;p&gt;No files on disk. &lt;br&gt;
No background services polling network sockets. &lt;br&gt;
Zero runtime packages added to your &lt;code&gt;package.json&lt;/code&gt; or &lt;code&gt;go.mod&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The operating system kernel manages the environment block, and &lt;strong&gt;your application never even knows MayFly was involved.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Chapter 5: The DX Pivot (Killing Syntax Friction)
&lt;/h3&gt;

&lt;p&gt;If you look at the commit log mid-way through the hackathon, our initial execution command looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mayfly run &lt;span class="nt"&gt;--&lt;/span&gt; npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked, but it felt clunky. The double hyphen (&lt;code&gt;--&lt;/code&gt;) is standard Unix convention for separating flags from positional arguments, but during day-to-day development, developers hate unnecessary syntax. &lt;/p&gt;

&lt;p&gt;Commit &lt;code&gt;b4a91f9&lt;/code&gt; marks a critical pivot in MayFly's design: &lt;strong&gt;transparent command interception.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We refactored the CLI argument parser so that MayFly inspects the first argument: if it matches a known subcommand (&lt;code&gt;init&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;, &lt;code&gt;scan&lt;/code&gt;, &lt;code&gt;audit&lt;/code&gt;), it runs internal management routines. But if it does not match an internal command, MayFly immediately assumes you want to execute code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Before:&lt;/span&gt;
mayfly run &lt;span class="nt"&gt;--&lt;/span&gt; npm run dev

&lt;span class="c"&gt;# After:&lt;/span&gt;
mayfly npm run dev
&lt;span class="c"&gt;# Or with the 2-letter alias:&lt;/span&gt;
mf npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By removing the syntax hurdle, MayFly stopped feeling like a cumbersome security wrapper and started feeling like a native system tool.&lt;/p&gt;




&lt;h3&gt;
  
  
  Chapter 6: The Cross-Platform Trenches
&lt;/h3&gt;

&lt;p&gt;Everything ran cleanly on Linux and macOS. Then our team member Alvi started running the test suite on Windows.&lt;/p&gt;

&lt;p&gt;What followed was a marathon of low-level edge cases that you never encounter until you strip away external helper libraries:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Windows Raw Mode:&lt;/strong&gt; Unix relies on POSIX &lt;code&gt;ioctl&lt;/code&gt; calls with &lt;code&gt;TCGETS&lt;/code&gt;/&lt;code&gt;TCSETS&lt;/code&gt; to clear the terminal echo flag. Windows consoles do not implement POSIX termios. We had to interact directly with &lt;code&gt;kernel32.dll&lt;/code&gt; using Go's &lt;code&gt;syscall&lt;/code&gt; package to manipulate console modes by hand: clearing &lt;code&gt;ENABLE_ECHO_INPUT&lt;/code&gt; while enabling &lt;code&gt;ENABLE_VIRTUAL_TERMINAL_INPUT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal Interrupt Signals:&lt;/strong&gt; If a user pressed &lt;code&gt;Ctrl+C&lt;/code&gt; 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 &lt;code&gt;os/signal&lt;/code&gt; to intercept interrupts and guarantee original console modes were restored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRLF Line Endings:&lt;/strong&gt; Windows terminal password prompts appended &lt;code&gt;\r\n&lt;/code&gt; instead of &lt;code&gt;\n&lt;/code&gt;, silently injecting carriage returns into master passwords and causing cross-platform decryption failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The PowerShell UTF-8 BOM Trap:&lt;/strong&gt; 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 (&lt;code&gt;\xef\xbb\xbf&lt;/code&gt;). 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.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h3&gt;
  
  
  Chapter 7: If You Touch It, the Math Breaks
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Instead of bundling an embedded database engine, we built a cryptographic audit log directly on top of standard file I/O and &lt;code&gt;crypto/sha256&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Every time MayFly decrypts, injects, or updates a key, it appends a structured entry to a local ledger (&lt;code&gt;~/.mayfly/audit.log&lt;/code&gt;). Each entry calculates its own digest by combining its metadata with the hash of the entry before it:&lt;/p&gt;

&lt;p&gt;$$\text{Block Hash}&lt;em&gt;n = \text{SHA256}(\text{Block Hash}&lt;/em&gt;{n-1} + \text{Timestamp} + \text{Operation} + \text{ProjectID})$$&lt;/p&gt;

&lt;p&gt;If an attacker tries to delete a line, edit a past timestamp, or cover their tracks, every subsequent hash in the chain breaks. Running &lt;code&gt;mf audit verify&lt;/code&gt; instantly flags the exact record that was manipulated.&lt;/p&gt;

&lt;p&gt;We applied this exact same paranoia to our installer and in-place self-updater (&lt;code&gt;mf update&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Rather than blindly executing remote scripts, both the shell installer and the Go binary stream cryptographic checksums (&lt;code&gt;checksums.txt&lt;/code&gt;) 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. &lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h3&gt;
  
  
  Chapter 8: Completing the Arsenal
&lt;/h3&gt;

&lt;p&gt;In the final hours before submission, we rounded out MayFly to handle the entire developer security lifecycle without needing external tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Built-in Leak Scanner &amp;amp; Git Hook (&lt;code&gt;mf scan&lt;/code&gt;):&lt;/strong&gt; Instead of pulling in &lt;code&gt;gitleaks&lt;/code&gt; or &lt;code&gt;trufflehog&lt;/code&gt;, we wrote a static analysis scanner using Go's &lt;code&gt;filepath.WalkDir&lt;/code&gt;, &lt;code&gt;bufio.Scanner&lt;/code&gt;, and compiled regex patterns. It catches exposed AWS keys, OpenAI tokens, connection strings, and Dockerfile secrets before they get committed. Running &lt;code&gt;mf install-hook&lt;/code&gt; installs a pre-commit hook directly into &lt;code&gt;.git/hooks/&lt;/code&gt; in one second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 1-Second &lt;code&gt;.env&lt;/code&gt; Shredder (&lt;code&gt;mf import --delete&lt;/code&gt;):&lt;/strong&gt; Migrating to MayFly shouldn't be tedious. Running &lt;code&gt;mf import&lt;/code&gt; ingests an existing &lt;code&gt;.env&lt;/code&gt; directly into the encrypted vault, and the &lt;code&gt;--delete&lt;/code&gt; flag securely shreds the plaintext file off your disk immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intermediate Memory Zeroing:&lt;/strong&gt; We added explicit memory zeroization routines (&lt;code&gt;pkg/executor&lt;/code&gt; and &lt;code&gt;pkg/vault&lt;/code&gt;) so that intermediate cryptographic keys and decrypted secrets are wiped from RAM arrays the moment operations finish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encrypted Disaster Recovery (&lt;code&gt;mf backup&lt;/code&gt;):&lt;/strong&gt; Because secrets never touch disk in plaintext, teams still need safe ways to migrate or backup their vaults. Running &lt;code&gt;mf backup&lt;/code&gt; exports a password-protected, encrypted snapshot of your projects and keys that you can safely restore anywhere.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Epilogue: What 72 Hours of Zero Dependencies Taught Us
&lt;/h3&gt;

&lt;p&gt;Building without external dependencies shifts your entire development perspective.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Every dependency pulled into a project is code written by someone else, running with your permissions, and granted access to your machine.&lt;/p&gt;

&lt;p&gt;By the time the submission window closed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our &lt;code&gt;go.mod&lt;/code&gt; file contained zero external requirements.&lt;/li&gt;
&lt;li&gt;The binary compiled down to a single self-contained executable for Linux, macOS, and Windows.&lt;/li&gt;
&lt;li&gt;Plaintext credentials were completely eliminated from project directories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also gave us an unexpected realization: having zero dependencies meant MayFly had zero external attack surface. &lt;strong&gt;The very tool built to protect you from supply chain poison was immune to it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MayFly GitHub:&lt;/strong&gt; &lt;a href="https://github.com/vishnunandan555/mayfly" rel="noopener noreferrer"&gt;vishnunandan555/mayfly&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MayFly Documentation:&lt;/strong&gt; &lt;a href="https://mayfly-docs.vercel.app/" rel="noopener noreferrer"&gt;mayfly-docs.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L.I.O.N GitHub:&lt;/strong&gt; &lt;a href="https://github.com/A56-A5/lion" rel="noopener noreferrer"&gt;A56-A5/lion&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built by &lt;strong&gt;vishnunandan555&lt;/strong&gt;, &lt;strong&gt;A56-A5&lt;/strong&gt;, and &lt;strong&gt;WanderingHumanid&lt;/strong&gt; for the &lt;strong&gt;Hackathon Raptors Zero Dependency Hackathon&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>go</category>
      <category>programming</category>
      <category>security</category>
    </item>
    <item>
      <title>I Built an API to Showcase Top Contributors on GitHub READMEs</title>
      <dc:creator>Vishnu Nandan</dc:creator>
      <pubDate>Mon, 11 May 2026 16:14:40 +0000</pubDate>
      <link>https://dev.to/vishnunandan555/i-built-an-api-to-showcase-top-contributors-on-github-readmes-j7b</link>
      <guid>https://dev.to/vishnunandan555/i-built-an-api-to-showcase-top-contributors-on-github-readmes-j7b</guid>
      <description>&lt;p&gt;I wanted a simple way to &lt;strong&gt;showcase the top contributors across all my GitHub repositories&lt;/strong&gt; directly on my GitHub profile README.&lt;/p&gt;

&lt;p&gt;Something like contrib.rocks, but across all repositories..&lt;/p&gt;

&lt;p&gt;Surprisingly, I couldn't really find a clean solution for it.&lt;/p&gt;

&lt;p&gt;Most existing tools were either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repo-specific&lt;/li&gt;
&lt;li&gt;difficult to self-host&lt;/li&gt;
&lt;li&gt;dependent on live GitHub API scraping&lt;/li&gt;
&lt;li&gt;heavily rate limited&lt;/li&gt;
&lt;li&gt;or just abandoned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built one.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏆 Top Contributors API
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Website:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://top-contributors-api.vercel.app/" rel="noopener noreferrer"&gt;https://top-contributors-api.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it looks like:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3a9cx4fj9x5vl485fpq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv3a9cx4fj9x5vl485fpq.png" alt=" " width="798" height="284"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🤔 Why I Built It
&lt;/h2&gt;

&lt;p&gt;Originally, I just wanted this for my own GitHub profile.&lt;/p&gt;

&lt;p&gt;Something simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aggregate contributors across all repos&lt;/li&gt;
&lt;li&gt;render avatars nicely&lt;/li&gt;
&lt;li&gt;auto-update&lt;/li&gt;
&lt;li&gt;easy to embed&lt;/li&gt;
&lt;li&gt;no maintenance headaches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I assumed someone had already built it.&lt;/p&gt;

&lt;p&gt;Apparently not.&lt;/p&gt;

&lt;p&gt;Or at least not in the way I wanted.&lt;/p&gt;

&lt;p&gt;So this became one of those:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Fine, I'll build it myself.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ The Real Problem Wasn't the Image
&lt;/h2&gt;

&lt;p&gt;Rendering the image itself was easy.&lt;/p&gt;

&lt;p&gt;The painful part was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;iterating through repositories&lt;/li&gt;
&lt;li&gt;merging contributor data&lt;/li&gt;
&lt;li&gt;pagination&lt;/li&gt;
&lt;li&gt;GitHub API rate limits&lt;/li&gt;
&lt;li&gt;serverless execution limits&lt;/li&gt;
&lt;li&gt;caching&lt;/li&gt;
&lt;li&gt;keeping response times fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first I tried doing everything live inside a Vercel API route.&lt;/p&gt;

&lt;p&gt;Bad idea.&lt;/p&gt;

&lt;p&gt;If a profile has enough repositories, the function can easily hit execution limits. And repeatedly querying GitHub on every request is basically asking to get throttled.&lt;/p&gt;

&lt;p&gt;So I redesigned the architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 How It Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. GitHub Actions Handles Aggregation
&lt;/h3&gt;

&lt;p&gt;A scheduled GitHub Actions workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scans public repositories&lt;/li&gt;
&lt;li&gt;aggregates contributor stats&lt;/li&gt;
&lt;li&gt;generates a lightweight &lt;code&gt;contributors.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;commits it back into the repository&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It uses GitHub’s built-in &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;, so setup stays extremely simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Vercel Only Renders the Image
&lt;/h3&gt;

&lt;p&gt;The API route:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reads the JSON file&lt;/li&gt;
&lt;li&gt;renders contributor avatars using &lt;code&gt;canvas&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;returns a PNG image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fast response times&lt;/li&gt;
&lt;li&gt;edge caching&lt;/li&gt;
&lt;li&gt;minimal GitHub API usage&lt;/li&gt;
&lt;li&gt;no live scraping during requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, using PNGs avoids GitHub README SVG sanitization weirdness, which was another unexpected rabbit hole.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔥 Two Versions
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Main Version (Recommended)
&lt;/h2&gt;

&lt;p&gt;Repository:&lt;br&gt;
&lt;a href="https://github.com/vishnunandan555/top-contributors-api" rel="noopener noreferrer"&gt;https://github.com/vishnunandan555/top-contributors-api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This version is meant for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;self-hosting&lt;/li&gt;
&lt;li&gt;private usage&lt;/li&gt;
&lt;li&gt;maximum reliability&lt;/li&gt;
&lt;li&gt;zero public API dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Fork the repository&lt;/li&gt;
&lt;li&gt;Enable GitHub Actions&lt;/li&gt;
&lt;li&gt;Run the &lt;code&gt;Aggregate Top Contributors&lt;/code&gt; workflow once&lt;/li&gt;
&lt;li&gt;Deploy to Vercel&lt;/li&gt;
&lt;li&gt;Use your Vercel Link&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No environment variables required.&lt;br&gt;
This avoids public rate limits entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  OTG Edition
&lt;/h2&gt;

&lt;p&gt;This version provides a public API.&lt;br&gt;
(you can find it in the website)&lt;/p&gt;

&lt;p&gt;You just:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;place your GitHub username in the URL&lt;/li&gt;
&lt;li&gt;paste it into your README&lt;/li&gt;
&lt;li&gt;done&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perfect for quick setup.&lt;/p&gt;

&lt;p&gt;Because it's a shared public instance, it &lt;em&gt;can&lt;/em&gt; get rate limited under heavy usage, although in normal usage it usually works fine.&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Future Plans
&lt;/h2&gt;

&lt;p&gt;Currently the output is a static image.&lt;/p&gt;

&lt;p&gt;One thing I really want to add is interactive contributor avatars, so clicking a contributor directly opens their GitHub profile.&lt;/p&gt;




&lt;h2&gt;
  
  
  📎 About Contribution Calculation
&lt;/h2&gt;

&lt;p&gt;Currently, contributors are ranked using aggregated commit counts across repositories via GitHub’s contributors API.&lt;/p&gt;

&lt;p&gt;Is commit count a perfect measure of contribution? &lt;strong&gt;Not really.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It ignores things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;code reviews&lt;/li&gt;
&lt;li&gt;issue triage&lt;/li&gt;
&lt;li&gt;mentoring&lt;/li&gt;
&lt;li&gt;discussions&lt;/li&gt;
&lt;li&gt;documentation/design work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But commits are the &lt;strong&gt;most practical&lt;/strong&gt; metric GitHub exposes that is fast, scalable, and reliable for an automated README widget.&lt;/p&gt;

&lt;p&gt;In the future, &lt;strong&gt;I’d like to experiment with a smarter scoring system&lt;/strong&gt; that can better &lt;strong&gt;represent contribution quality&lt;/strong&gt; instead of just raw commit volume.&lt;/p&gt;

</description>
      <category>github</category>
      <category>api</category>
      <category>opensource</category>
      <category>readme</category>
    </item>
  </channel>
</rss>
