The third post today is a quieter one. The earlier two — the restore test that caught a real bug and the twenty-nine-commit trip budget — covered the visible work. This one is about a single commit, fifteen lines changed, that codifies a decision the last two weeks of patch-cadence posts were quietly converging on.

cbb7fa4: adr-0001: promote Authentik from Tier B to Tier A.

That’s the whole commit. Twelve insertions, three deletions, in docs/adr/0001-image-pinning-policy.md. No code touched. But it changes the contract for how every Authentik bump will be handled going forward, and it’s worth writing down why — because the why is something I would not have been able to articulate two weeks ago.

What Tier B was for

The pinning policy splits images into three tiers. Tier A is repo/image:X.Y.Z — full version, no automatic anything, human review on every bump. Tier B is repo/image:X.Y-variant — minor-line tracking, automatic patch updates on container restart. Tier C is digest-pinning, reserved for upstreams that have surprised us by re-publishing the same semantic tag.

The deal Tier B offers is a real trade: you give up per-patch operator review in exchange for not having to babysit base-image security fixes. For something like nginx:1.27-alpine, that’s an obviously good trade — nginx ships CVE fixes inside minor lines all the time, the API surface inside a minor is genuinely stable, and the patches are usually pulling forward a hardened-OpenSSL or a libxml2 bump. We want those. We don’t want a ticket per CVE asking us to bump nginx:1.27.1 to nginx:1.27.2.

Authentik was originally a Tier B pick for the same reason. The upstream ships security patches inside their minor lines (2026.2.x, 2026.5.x, etc.) and we wanted them. The goauthentik/server:2026.2 tag would float forward as 2026.2.1, 2026.2.2, 2026.2.3 landed.

That deal looked clean when I wrote the ADR. It looked less clean two weeks later when I sat down to do the 2026.2.3 → 2026.5.0 upgrade and read the changelog.

What 2026.5 actually shipped

A single minor bump — 2026.2 to 2026.5 — included three behavior changes that have nothing to do with CVEs:

  1. The default listening IP changed from 0.0.0.0 to [::]. In most environments that’s a no-op because the dual-stack socket covers IPv4 too. In a Podman bridge network with explicit PublishPort mappings, it’s a depends-on-the-day situation. The reason it didn’t break server01 is that the Quadlet explicitly publishes against the bridge alias, but it’s exactly the kind of default-change that should land in a release-notes pass before deployment, not at 2am after a restart fails its smoke test.
  2. A policy flag was renamed. policies_buffered_access_view was removed. If you’d referenced it anywhere — in a custom flow, in a template, in an embedded outpost config — that reference now silently no-ops or errors depending on context. We don’t reference it. But “we don’t reference it” is something I have to check, not something Tier B tracking can check for me.
  3. Seventeen internal packages were removed. Most are not interesting. A few are the kind of thing a forward-auth path might transitively depend on through a custom branding or theming customization. Again, we don’t have anything custom enough to be affected. Again, “we don’t” is a thing I have to verify, not assume.

None of these are bugs. They’re all defensible upstream choices. They’re all the kind of choice that, in a long-lived production deploy of identity infrastructure, you want a human reading the release notes for before the restart command runs.

The blast-radius argument

Identity infrastructure is qualitatively different from a backing database or a static-asset proxy. Authentik gates access to every other application on ourhomeport.com via forward-auth — Termix, BentoPDF, the new OurBudgetTracker that ships tomorrow, eventually Actual Budget and ezbookkeeping once their migrations finish. If Authentik’s listening default flips and the outpost can’t reach upstream after a restart, every gated app is unreachable at the same moment. The recovery path is exactly the same as a normal restart cycle, but the blast radius during the window is the entire ourhomeport.com namespace.

That’s the asymmetry. A bad nginx patch breaks one vhost. A bad Authentik patch breaks the front door for the whole house.

The ADR text I wrote into §3 today is two lines:

  1. Identity infrastructure has high blast radius. Authentik gates access to every other application via forward-auth. A surprise behavior change there has cascading consequences — every patch deserves explicit operator review.
  2. Authentik minors ship behavior changes beyond CVE fixes. 2026.5 alone shipped a listening-IP default change, a policy-flag rename, and 17 package removals. The 3-month minor cadence makes “auto-track latest minor patches” not a clean fit.

The first one is the principle. The second one is the evidence that made me believe the principle applies here.

The cleanup that came with it

Three Quadlets on the OurHomePort side still had AutoUpdate=registry on them — postgres-ezbookkeeping, postgres-n8n, nginx-proxy. All three came from older deploys, predating the ADR sweep. None of them were causing visible harm, because Podman’s auto-update timer wasn’t running aggressively enough on server01 to have surprised us, but they all violated §2 of the same ADR (no automatic updates from registry).

That sweep landed in commit 0cfc902 earlier today. Both repos now satisfy the ADR completely: every Image= line has either an explicit X.Y.Z tag or an intentional Tier B X.Y-variant, and zero AutoUpdate=registry lines remain anywhere.

The follow-up note at the bottom of the ADR’s implementation snapshot now reads:

2026-05-23 follow-up: Authentik (goauthentik/server:2026.5.0) promoted from Tier B to Tier A; see Tier B narrative above for rationale. Same day, three remaining AutoUpdate=registry lines on OHP Quadlets (postgres-ezbookkeeping, postgres-n8n, nginx-proxy) were removed — every Quadlet in both repos now conforms to §1 + §2.

Tonight’s research digest flagged two Authentik CVEs from the latest disclosure batch — CVE-2026-40165 (SAML NameID parsing, CVSS 8.7) and CVE-2026-40166 (OAuth2 client_secret exposure, CVSS 7.1). Both fixed in 2025.12.5 / 2026.2.3.

The digest verified the running version on server01 before flagging anything. authentik-server:2026.5.0. Past the fix on both. No issue filed.

That’s the pattern that’s been showing up in basically every digest run this month, and it’s the one I keep being quietly grateful for. The naive workflow — “CVE published, web search for fix version, file ticket” — would have created two issues tonight that didn’t need to exist. The verify-first workflow caught both, plus the CopyFail kernel one, plus the Wazuh 4.14.4 one, plus the Podman 5.6 Windows-only one. Four would-have-been tickets averted in a single digest run.

The Tier A promotion makes that pattern more important, not less, because every Authentik bump from here on is going to be a deliberate human decision rather than a pulled tag. Having the digest pre-filter to “is this CVE actually present in our running version” turns each bump from “is there a CVE we missed?” into “is there a CVE we should plan around?” That’s a cheaper question to answer.

What changed at the deploy level: nothing

This is worth saying explicitly, because someone reading the commit might assume a policy change implies a deploy change. It doesn’t. The image tag in the Quadlet was already goauthentik/server:2026.5.0 before the ADR commit landed — the upgrade happened on May 22, the ADR commit just codifies what tier that pin shape belongs to going forward.

The practical change is for the next bump. The next time Authentik ships a 2026.5.1, it won’t float forward when the container restarts. It’ll require a Quadlet edit, a scp, a systemctl --user restart, and a smoke-test against the front door. The deploy is slower by maybe twenty minutes. The variance in deploy outcomes is going to be a lot smaller. That’s the whole trade.