Switching from GPG to age

It’s been several years since I went through all the trouble of setting up my own GPG keys and securing them in YubiKeys following drduh’s guide. With that approach, you generate one key securely offline and store it on multiple YubiKeys, along with a backup.

It has worked well for me for years, and as the Lindy effect suggests, it would almost certainly continue to.

But as my sub-keys were nearing expiration, I was faced with either renewing (more convenient, no forward secrecy) or rotating them (rather painful, but potentially more secure). However, I’ve realized that I essentially only use these keys for encryption, and almost never for signing. So, instead of doing either of the usual options, I’m going to let my keys expire entirely.

I’m now experimenting with age, which touts itself as “simple, modern, and secure encryption”. If needed, I will use minisign for signatures.

Workflow changes

This required changing a couple of things in my typical workflow.

Password manager

First, and foremost, I needed to switch from pass to passage, a fork of pass that uses age as the backend. This was actually surprisingly easy because passage includes a simple bash script to do the migration.

#! /usr/bin/env bash
set -eou pipefail
cd "${PASSWORD_STORE_DIR:-$HOME/.password-store}"
while read -r -d "" passfile; do
  name="${passfile#./}"; name="${name%.gpg}"
  [[ -f "${PASSAGE_DIR:-$HOME/.passage/store}/$name.age" ]] && continue
  pass "$name" | passage insert -m "$name" || { passage rm "$name"; break; }
done < <(find . -path '*/.git' -prune -o -iname '*.gpg' -print0)

There is no installer for passage, and no Arch packages. But it’s easy enough to install because it’s just a shell script you can throw on your $PATH. Note that for Arch, I also needed to install tree, which it assumes you have.

I also name passage as pass on my machine.

The benefit of this is everything that had pass integration has continued to “just work”. For example, aerc, my email client of choice, behaved exactly the same after the migration.

No more gpg-agent

I would occasionally use gpg-agent as my SSH agent on my machines. It was convenient. However, I also like the idea of having a dedicated SSH key per machine. It makes monitoring their usage and revoking them much finer-grained.

The absence of gpg-agent forced me to set up new keys on all my machines and add them to various servers/services.

Benefits of the age approach

Easy encryption with chezmoi

While I was tending to the encryption area of my personal tech “garden”, I also started leveraging chezmoi’s encryption features. I already use chezmoi for my configuration files, but with encryption, I could also easily add “secrets” to my public dotfiles repo. In my case so far, this just means my copies of my favorite paid font: Berkeley Mono.

chezmoi also has a nice guide for configuring chezmoi to encrypt while asking for a passphrase only once for age.

Easy YubiKey configuration

I was also very pleasantly surprised with how easy it was to switch to age! Last time I set up the GPG keys on my YubiKeys, I spent several hours.

This time, with the help of age-plugin-yubikey and embracing the idea of having unique keys on each YubiKey, but encrypting everything for multiple recipients, setting up my keys was surprisingly trivial. It also generates the keys securely on the hardware key itself, which is nice. The whole process probably took 30 minutes. It was so easy that in the future, I’m very much not intimidated by the thought of rotating keys.

Thoughts

Did I need to switch to age? Of course not.

However, over my career, I repeatedly find that exploring new tools for your core workflows (part of investing in interfaces) is just plain fun. I often learn new ways of thinking about problems. Sometimes, you walk away with a new default that brings some fresh ideas and some delight to your life. Other times, you walk away with your trusty old tool, with greater appreciation for its history and the hard-earned approach it has established. For my uses at the moment, age definitely falls into the former camp.

Tip

This post made it to Hacker News. See the thread for some additional discussion.

Posts from blogs I follow

The Past, Present, and Future of Digital Storytelling with Ramy Katrib

Ramy Katrib has been at the forefront of digital storytelling for over 25 years, with his post-production company, DigitalFilm Tree, earning a reputation as one of Hollywood's most technically adventurous post-production houses. He joined Bryan and Adam to explore the past, prese…

via Oxide and Friends July 23, 2026

On AI

In 2019, I started watching Andreas Kling's programming videos, many of which were live-coding sessions writing his new operating system Serenity OS. He was always pretty fast at writing code, especially since his IDE, Qt Creator, had knowledge of all his C++ code and could auto-…

via joshua stein July 23, 2026

Hardening Rust Code For Production

We talked about patterns for defensive programming in Rust before, in which implicit invariants that aren’t enforced by the compiler lead to utter misery. But being careful isn’t enough! Even valid code can fail at runtime in ways that are hard to predict and control. That’s what…

via Corrode Rust Consulting July 23, 2026

Generated by openring-rs from my blogroll.