Skip to content
Thomas BerdyThomas Berdy
July 15, 2026 · 6 min read

Our Auth Library's Last Release Was July 2022. We Noticed in June 2026.

Our Auth Library's Last Release Was July 2022. We Noticed in June 2026.

A dead library ran our dashboard's authentication for four years without a single alert, because dependency bots only report new versions and known CVEs.

I was reviewing a teammate’s PR against our dashboard’s authentication code when I hit an import I’d never seen: imia. I got curious and opened the upstream repo. It was archived, and its last release dated back to July 2022.

imia ran session authentication for the Mergify dashboard: every login and every staff impersonation. We adopted it in June 2022, when it looked alive; its final release shipped a few weeks later. It held that job for four years while upstream sat still, and nothing in our tooling ever said a word.

Nothing warned us

Our bots watch dependencies all day, but everything they show us is one of two things: a new version to bump to, or a published vulnerability to patch. An archived project rarely produces either. There’s no release to bump to, and if a vulnerability turns up, there’s no maintainer to write the fix. Your bot never gets a patched version to offer you. The pin keeps resolving and CI stays green, so the silence reads as health.

When I asked myself how long we would have kept shipping on top of it without that lucky code review, the honest answer was months, maybe years.

The impact analysis I wrote that morning wasn’t dramatic, which is what makes this failure mode nasty. The pinned wheel still installs. Nothing breaks tomorrow. The costs are all deferred: a security issue in our auth layer would go unreported and unfixed, and imia 0.5.3 only keeps working with current Starlette because it sticks to Starlette APIs that have stayed stable so far, so a Starlette release would eventually pick the migration deadline, not us.

There’s a fair objection that a small library over stable APIs might be finished rather than abandoned, and for a string-padding helper I’d accept it. The layer that decides who is logged in doesn’t get that benefit of the doubt.

The fix I refused

The first version of our migration ticket proposed vendoring imia. It’s small (about 680 lines) and MIT-licensed, so copying it into our tree was the path of least resistance.

I killed that plan. Authentication is one of the most common building blocks in software, and the community maintains good libraries for it; owning a private copy of auth code that nobody else reads or patches felt like the worst of both worlds. Forking it publicly would have been the same commitment with extra ceremony. Vendoring can make sense for a leaf utility. For the code that decides who is logged in, I want as much shared, maintained code under it as possible.

So we listed the real options:

OptionWhy we passed
fastapi-usersWants to own the user model and ships password machinery we’d never use (our login is GitHub OAuth only). Our Redis-backed session flow would end up as custom strategies anyway.
AuthXJWT-based. Instant revocation means bolting a token blocklist back on, which is the server-side state our Redis sessions already give us.
Starlette-Login15 stars, one maintainer. That’s how we got into this situation.
Pin and monitorKeeps every risk and hands the migration date to a future Starlette release.

We picked starlette.authentication, the auth layer Starlette itself ships, plus our own adapters on top.

Which, if you squint, is exactly what imia was: thin glue over Starlette primitives. The custom code just moved down a level, onto the framework we already pin and track. It shrank to a couple hundred typed, tested lines that we own.

Swapping auth middleware without logging anyone out

A middleware-stack swap doesn’t realistically fit behind a feature flag: the middleware is either in the stack handling every request or it isn’t. Safety had to come from how we designed the change.

The decision that mattered most: keep the session storage exactly as it was. Sessions live in Redis via starsessions (same author as imia, but still maintained), and the new backend reads the same session keys imia wrote. That bought us what a feature flag or a dual-stack dispatcher would have: every session opened before the deploy stayed valid after it, and rolling back would have been an ordinary redeploy against the same sessions. Nobody got logged out, and nobody even noticed.

The swap turned out to be a simplification too. imia needed two middlewares chained together, one for authentication and one for impersonation. Starlette’s built-in AuthenticationMiddleware takes a backend, and ours resolves both in one pass: load the user from the session, then swap in the impersonated user when a staff member is driving.

Before: two imia middlewares chained

flowchart LR
    A1[Request] --> B1[imia AuthenticationMiddleware]
    B1 --> C1[imia ImpersonationMiddleware]
    C1 --> D1[Dashboard app]

After: one Starlette middleware, our backend

flowchart LR
    A2[Request] --> B2[starlette AuthenticationMiddleware]
    B2 --> C2["Mergify backend<br/>session user + impersonation in one pass"]
    C2 --> D2[Dashboard app]

The rest was mechanical: port the existing auth tests to the new backend, then walk through login, logout, and entering and exiting impersonation on staging. Our own PR-risk bot flagged the change as HIGH (about 1,200 lines across 17 files, touching the API layer), which felt fair for an auth swap. It merged three days after I first saw the archive banner.

Making luck less necessary

Finding this through idle curiosity in a code review is not a process. The frustrating part is that the data to catch it was sitting in public APIs: the registries have exposed the stale release dates since 2022, GitHub exposes an archived flag, and OpenSSF Scorecard even scores maintenance health from signals like these. None of that was wired into anything we actually look at.

So the same morning, I opened a second ticket: an auditor that walks our direct dependencies and pulls those signals into a ranked report. Archived or deprecated projects come first, then stale-but-alive ones. A single-maintainer project sitting on a sensitive path like auth or session handling gets extra weight.

That rule cuts both ways: starsessions, the same author’s session library we kept, sits on that watchlist. It isn’t abandoned, and this time something is watching instead of waiting for luck.

The first run flagged a handful of other dependencies that need care. We now have a ranked list instead of an accident.

What it can’t measure is the thing I actually want to know. Registries record versions and vulnerabilities, and GitHub records activity, but there’s nowhere a maintainer can say how much time they still have for a project. Every check we run (release recency, commit dates, archived flags, deprecation banners) is a proxy for one person’s available attention. imia’s author did the responsible thing by archiving the repo, loudly and unambiguously. Plenty of dying projects never get that. They go quiet, and your bots keep telling you everything is fine.

Go check yours. It takes five minutes to grep your lockfile for the libraries sitting under your login flow and open their repos. I found four years of silence under ours.

Stay ahead in CI/CD

Blog posts, release news, and automation tips straight in your inbox.

Recommended posts

Two counters instead of a materialized view
July 3, 2026·10 min read

Two counters instead of a materialized view

Materialized views recompute from source data, but we delete ours within 30 days. Here's the two-writer, two-counter rollup we built on plain Postgres, and the race a boolean dirty flag would have quietly lost.

Rémy DuthuRémy Duthu
ON CONFLICT DO UPDATE Is Rewriting Rows You Never Changed
June 19, 2026·13 min read

ON CONFLICT DO UPDATE Is Rewriting Rows You Never Changed

A bare INSERT ... ON CONFLICT DO UPDATE rewrites the whole row even when nothing changed. Production numbers showing what that costs, the one-line WHERE clause that stops it, and the 21 call sites where we had to leave the write alone.

Mehdi AbaakoukMehdi Abaakouk
A Disk Alert, a 392 GB Table, and Indexes Bigger Than the Data
June 14, 2026·8 min read

A Disk Alert, a 392 GB Table, and Indexes Bigger Than the Data

A routine Postgres disk alert turned into more than 150 GB of reclaimable waste: blobs nobody reads back, the same JSON copied across 200 million rows, and indexes larger than the data they index.

Mehdi AbaakoukMehdi Abaakouk