Featured image of post The gpg command that hung (so I built signing into the tool)

The gpg command that hung (so I built signing into the tool)

This one starts at the day job, not the side projects. My employer ships Go products and wants to move quickly on them, which is reasonable, but nobody wants to hand-maintain a fresh pile of the same boilerplate every time a new service spins up. I’d had go-tool-base running as a product of my own for a while by then, solving exactly that, so I put my hand up: use this, and the boilerplate stops being your problem.

There was a condition, and it was the right one. To go anywhere near our codebase it had to clear the security bar, and ours is not a low bar. So I set about hardening the thing properly, layer by layer, until it could pass the regulations the business actually has to answer to rather than the ones a side project can wave away. One of those layers, unavoidably, is signing your releases. If you’re shipping binaries into an enterprise, “trust me, I built it” is not a supply-chain story anyone signs off on.

Which is how I came to be generating a signing key by hand with gpg. Or trying to.

The incantation is the sort of thing you copy from your own notes from the last time, feed it the right flags, and walk away from while it does its business. So I did. And it just… hung. No key. I went round the houses. --list-secret-keys came back empty while --list-keys cheerfully showed the public half. GNUPGHOME had the public part and not the secret one, which is a contradiction in terms if you think about it for more than a second. I was, in short, fighting gpg’s environment rather than doing the actual work, and getting nowhere.

I’ll spare you the precise sequence of me being wrong, because the sequence isn’t the point. The point arrived somewhere in the middle of it, and it changed the whole approach.

If I’m fighting gpg here, on my own machine, with my own notes, then every single team that adopts this framework is going to fight gpg too.

That’s the thing about distributing a tool, especially into a place where ten teams will pick it up rather than one. The boilerplate they didn’t want to maintain… that wasn’t just the HTTP wiring and the config plumbing. It was this. The fiddly, environment-dependent, fails-in-a-different-way-on-every-machine business of producing a signing key. Push that onto every team and you haven’t given them a framework, you’ve given them a homework assignment with a security deadline.

So it moved inside the box. Key generation and minting became commands in the binary itself: gtb keys generate to produce a fresh keypair, gtb keys mint to turn it into the signing key the release process wants, no shell-out to gpg anywhere in the path. The gpg backend I’d started with got dropped entirely rather than papered over. The tool now does the awkward part the same way on every machine, because it’s the same code doing it every time, not a conversation with whatever gpg thinks GNUPGHOME means today.

The actual cryptography underneath is still real OpenPGP, and the question of how you mint a key whose private half never leaves a hardware module is its own story. This isn’t really about the crypto. It’s about where you put the sharp edges.

When you build a tool for yourself, you can live with the dangerous bits. You learn where they are, you work around them, and they become invisible. When you hand that tool to an enterprise full of people who’ve never met it, everything you left out in the open is a support ticket, a stalled rollout, or worse, a team deciding signing is too much hassle and skipping it. Keeping the sharp edges inside the box isn’t polish. For this, it was the difference between a security control people use and one they route around.

The best thing I shipped that week wasn’t a cleverer algorithm. It was a key that couldn’t fail to generate. Unexciting, I’ll grant you. But an unexciting key you actually have beats a clever one you’re still fighting at 11pm.

Built with Hugo
Theme Stack designed by Jimmy