Let’s talk about the two hours on Tuesday when the software sitting in the middle of everybody’s AI stack was quietly handing out the keys.
Here’s what happened.
At 11:39 UTC on 21 October, version 1.107.0 of LiteLLM landed on PyPI. LiteLLM is the proxy a lot of teams run so their apps can talk to OpenAI, Anthropic, Google, Bedrock, whoever, through one door instead of five. It’s popular in the unglamorous way — more than a hundred million downloads on PyPI last month, north of two thousand packages depending on it, many of them without anyone typing its name.
All seven wheels in that release carried malware. So did the official Docker images, including whoever pulled latest in that window. The GitHub source was clean. The build wasn’t.
Two payloads. One was a credential stealer that ran the moment Python started — not when you imported LiteLLM, when Python started, so your app, your pip, your IDE’s language server, your CI job. It took SSH keys, GPG keys, TLS private keys, AWS and Azure and GCP credentials, Kubernetes tokens, GitLab runner configs, Jenkins master keys, browser passwords, .env files, and the configs of more than fifteen AI coding tools.
The second payload forked a daemon that checked a GitHub file every hour and ran whatever code the attacker pointed it at.
It posted the loot to litellm.dev. Not litellm.ai.
PyPI quarantined it at 13:23. Under two hours. And it was the second time LiteLLM got hit this year — the first was March, versions 1.82.7 and 1.82.8.
That same morning, Railway disclosed that someone had reached systems holding customer environment variables and, for some accounts, got the decrypted contents. API keys. Database credentials. They paused every deployment while they worked it out. Their read: this is part of a series of attacks aimed at platform companies in order to compromise AI inference providers.
Sit with that for a second. The target wasn’t your photos. It was the keys to the meter.
Picture the setup, because it’s most of us.
A developer — probably a good one, probably tired — gets handed five repos that each have an OpenAI key and an Anthropic key pasted into a .env file, plus a stray Google key somebody added in a hurry. Keys in three places, nobody knows which one is live, one of them is in a Slack message from February.
So they do the sensible thing. They stand up LiteLLM on a small box, point everything at it, and put all the provider keys in one table so the app only ever holds one internal token. Cleaner. Auditable, in theory. One place to change a key. Ten minutes of work that removes a real mess.
That table is called LiteLLM_CredentialsTable. The stealer went straight for it. Not sideways, not opportunistically — there’s a module named db_litellm in the payload whose entire job is to dump that table.
So the tidy solution and the attack surface were the same object. That’s not carelessness. That’s the shape of the problem. Centralising keys is correct engineering, and centralising keys is exactly what makes one bad afternoon catastrophic.
Now the part people are skipping.
Every write-up I read on this is about supply chain. Pin your versions. Use provenance attestations. Scan your dependencies. All true, all necessary, and all of it is about how the poison got in the door.
Almost nobody is talking about what was taken, and why it was worth taking.
An API key is not a password. A password authenticates a person, badly. An API key is a bearer instrument. It doesn’t prove who is asking. It proves only that whoever is asking has the string. There’s no second factor, no device, no location, no intent. There’s no name on it.
What it does carry is three things that matter more than most of what’s in your database.
Your money. A frontier model key is a spend authorisation. Someone with your key can burn your monthly budget in an afternoon running inference they never pay for, and the bill arrives at your company, in your currency, against your card.
Your voice. Every call made with that key comes out of your account, with your model configuration, your system prompt if it lives in the proxy, your business’s name attached to whatever it generates. If someone uses your key to produce fraud at scale, the logs show you.
Your reputation with the provider. Which brings the ugly second-order effect.
When the abuse gets noticed, the first move is to shut the account off. Not the attacker’s account — yours. You wake up to rate limits, a suspension notice, a support ticket in a queue, and a very polite email asking you to explain the traffic. The person who lost the key becomes the suspect in their own account, and the burden of proving it wasn’t them lands on the only party with no logs.
That is the same failure we keep running into everywhere else. Authentication got mistaken for authority. A valid key proved the request was allowed to exist. Nobody asked whether it was allowed to happen.
There’s a detail in the technical write-ups that should bother you more than the malware.
PyPI has no provenance attestations for that release. For 1.106.2 either. The published artefact doesn’t carry a signed statement of which repository, which commit, which build pipeline produced it. Researchers had to reconstruct the story by comparing file hashes by hand and lining up zip timestamps — macOS build at 11:20, Linux at 11:21, Windows at 11:37. That’s forensics after the fact. That’s not how any of this should work in 2026.
The source was clean. The tag was clean. The thing you installed was not the thing you read. And there’s no signature on the box to tell you that.
Think about what that means outside the terminal.
We have spent two years arguing about whether AI content should be labelled, watermarked, credentialled, traceable to its origin. Meanwhile the artefacts that actually run the AI — the packages, the images, the keys, the agent tokens — are moving through the world with less provenance than a PDF someone exported from Word.
The label debate is downstream. The build is upstream. If you cannot prove what produced the thing you installed, the label on its output is decoration.
So what do you actually do. Not the security team’s list. Yours.
Inventory the keys. Not “we use OpenAI” — a list, with an owner, a creation date, and what it can reach. Most companies cannot produce this list, which is the whole reason a two-hour window is terrifying.
Scope them. One key per service, per environment, per agent. Never one key that can do everything, because a key that can do everything is a key whose theft is a company event.
Cap them. Hard spend limits per key, alerting at a threshold you’d actually notice, not the one that fires after the invoice.
Rotate on a schedule, and rotate once as a drill before you have to. The teams that came out of Tuesday intact weren’t the ones with better scanners. They were the ones who could answer “what did that host touch” in an hour.
And stop letting one table hold every provider key in the building. If the proxy must hold them, treat that database like a payment switch, because functionally it is one.
None of that is exotic. It’s the discipline banks have had for forty years about card numbers, applied to a credential that can spend money and speak in your name. We just didn’t apply it, because the key was free to create and the docs made it a one-liner.
The principle worth keeping is simple and it isn’t about malware.
When you hand capability to a machine, the delegation has to be scoped, capped, revocable, and receipted. Anything less and you haven’t delegated — you’ve handed over a bearer note and hoped for the best.
A key with no name on it is a promise that nobody will ever ask who was holding it. Tuesday was two hours of somebody else holding yours.
You’ve got this. Go check which keys that host could reach.