All articles
Dev ToolsBy Petru Popa · 5 min read

Your Supply Chain Has an Expiry Date. GitHub CLI's Is Tomorrow.

A PGP key that stops working on 5 September breaks apt update inside images that have not changed in months. The replacement key has no expiry at all.

GitHub published a changelog entry on 3 September saying the PGP key that signs its Linux gh packages expires on Saturday, 5 September 2026. The linked issue, cli/cli 13118, gives the two fingerprints: the expiring key is 2C6106201985B60E6C7AC87323F3D4EA75716059, the replacement is 7F38BBB59D064DBCB3D84D725612B36462313325. A keyring file containing both was published on 8 April 2026.

That is the whole announcement, and read as a package-manager notice it is unremarkable. Read as a software supply chain security event it is more interesting, because of who it hits: not people running gh on a laptop, but build images nobody has touched since before April. If a Dockerfile added the GitHub CLI repository once and a later layer runs apt update, that build stops working this weekend without a single line of your code changing.

The issue is specific about the blast radius. You are affected if you installed gh through apt, dnf, yum or zypper before 8 April 2026 and have not re-run the install steps since. You are not affected on Windows or macOS, or via Homebrew, Conda, a community package manager, a downloaded .deb, or a binary from GitHub Releases — none of those paths use the key.

The failure surfaces as EXPKEYSIG 23F3D4EA75716059 alongside NO_PUBKEY 5612B36462313325 on Debian and Ubuntu, and as a signature verification failure on the RPM side. Debian's own apt-secure page explains why that is fatal rather than noisy: apt verifies the signature on a repository's Release file, and Signed-By in a sources entry limits which keys are allowed to sign that archive. A repository whose signature does not verify is a repository apt will not take data from.

The RPM case has a second trap worth naming, because it is the one that survives a naive fix. Some configurations reference the key by a keyserver URL containing the old key ID 0x23F3D4EA75716059. Refreshing a keyring does nothing for those — the repository definition itself has to be re-added from https://cli.github.com/packages/rpm/gh-cli.repo, and the issue documents a further step of removing the stale gpg-pubkey entry from the RPM keyring when verification still fails afterwards.

What the key material says that nobody spells out

The published keyring is fetchable, so it can be read rather than trusted. Doing that with gpg --show-keys --with-colons on https://cli.github.com/packages/githubcli-archive-keyring.gpg returns two primary keys. The old one was created on 2022-09-06 and carries an expiry of 2026-09-05T12:43:48Z, with its signing subkey expiring 22 seconds later. The new one was created on 2026-04-07 and carries no expiration date at all — neither the primary key nor its subkey has one set.

The issue's own sample output shows this — the new key is printed without an expiry line, next to an old key printed with one — but neither the changelog nor the issue text says anything about it, and it is the detail that decides what kind of problem this is. The expiring key was on a four-year clock, and a clock is a thing you can put in a calendar. Its replacement has no clock. Nothing about the new key material will produce a dated warning the way this one did, so whatever eventually retires it will not arrive with a countdown attached.

Note also that this is the second expiry of this key, not the first. Issue 9569 records it expiring on 6 September 2024 at approximately 11:17 UTC and disrupting Linux installs and updates; the remedy then was to extend the existing key's expiration as an emergency fix. The key material still carries that history — a primary key created 2022-09-06 that now runs to 2026-09-05. This round was announced ahead of the date rather than after the breakage, which is the difference between the two events. What it does not change is the shape of the dependency: one tool, two dated trust expiries inside four years, both of which land on images rather than on people.

What to do this quarter

Today, in one command on any affected machine or image:

gpg --show-keys /etc/apt/keyrings/githubcli-archive-keyring.gpg

Two public key blocks mean you are covered. One means you are not. If the file is not there, check /usr/share/keyrings/githubcli-archive-keyring.gpg, or read the signed-by= value out of /etc/apt/sources.list.d/github-cli.list.

The quarterly decision is the part that outlives Saturday: build an inventory of every third-party trust anchor your images fetch, with its expiry date, and treat anything expiring inside twelve months as a scheduled outage you have already been warned about. Grep your Dockerfiles and provisioning scripts for every keyrings path and every rpm --import; each hit is a signature you have delegated to someone else's calendar. None of them announce themselves on the day they matter, because the failure mode is a build that has been green for a year and then is not.

This matters more, not less, on the images your agents and inference services run in. A CI image that installs a CLI tool at build time inherits that tool's key schedule, and the last mile of getting a system into production is exactly where this kind of undocumented dependency surfaces — the same class of gap covered in going from proof of concept to production. An agent that shells out to gh from an image built before April fails on Saturday for a reason that has nothing to do with the model.

What would show this is wrong

The claim here is falsifiable in a specific way: that trust-anchor expiry is a recurring, plannable class of build failure rather than a one-off chore. What would show it wrong is an inventory that comes back nearly empty — if you run that grep and find only one or two third-party keyrings, all of them with the same no-expiry shape as GitHub's replacement key, then there is no calendar to maintain and the correct response is to fix Saturday's break and move on. Run the grep before deciding which case you are in.

Ready to start?

Turn this into a plan for your team.

One week, fixed fee: a working session with your team, a prioritized use-case backlog, and an ROI model for the opportunities worth chasing.

Book an AI Opportunity Sprint