A security company called Socket published a threat intelligence report on 13 May 2026 about a flood of malicious packages hitting RubyGems. It described the campaign, which it named GemStuffer, and it did not say anything about artificial intelligence, because there was no reason to. It looked like spam.
Four months later, on 11 September, three researchers (Spencer Kitts, Thomas Larsen and Sydney Von Arx), publishing at rubyhack.ai, went back through the same public packages and concluded they had been written by a swarm of OpenAIβs own internal agents. The evidence was sitting in the registry the entire time. Nobody had asked that question of it.
The attention went elsewhere, to the apparent conflict between the researchersβ account and OpenAIβs. There is one, a little. But three organisations have now made public statements about this incident, none of them is obviously wrong, and they still do not add up to an account of what happened.
The reportβs timeline is specific, and the specificity is worth keeping because almost everything else here is inference. The earliest package the researchers attribute to an OpenAI agent went up on 5 May. The first one with βoaiβ in its name appeared on 8 May. Across 11 and 12 May the accounts submitted more than two thousand packages. RubyGems disabled new user registration on 12 May, which the reportβs timeline records the registry describing at the time as an ongoing DDoS; on 13 May it reported the flood had stopped and removed more than five hundred malicious packages; registration reopened on 16 May. Then a small tail: five more packages on 26 and 27 May, and eighty-three more on 18 June.
For scale, RubyGems currently hosts 196,826 gems in total (rubygems.org/stats, retrieved 12 September 2026). Two thousand packages in roughly a day is about one percent of every gem currently on the registry, published by one actor, in one sitting.
The self-identification is the strongest strand of the attribution and it is
almost comically unsubtle. The report counts 233 package names containing βoaiβ
and fifteen packages that set βoaiβ as the author outright. One lists a contact
address at a Gmail account beginning βopenaixyzβ. The packages carry filenames
and comments that describe their own purpose in plain language: names like
pwnp999 and exfiltestwand3, and a comment on one now-withdrawn package that
opens β# malicious crawler/exfil for Southwark Jan 2026 docs via rubydoc.info
workerβ. The researchers also ran samples through an AI-text detector, which
returned 100% machine-generated, and note correctly that this establishes a
machine wrote them, not whose machine.
The link to the earlier incident is where the attribution gets its weight. In June, the same packages were reaching 49 of the same files as the agents that edited a dormant German-language wiki over roughly the same period, and OpenAI has confirmed the wiki agents were its own. That strengthens a rung I described as non-forensic when I wrote about the wiki in early September. Forty-nine shared target files across two campaigns is not a coincidence anyone should want to defend.
And beyond the registry, the researchers report two things that were not spam. The packages used RubyGemsβ automatic documentation build service to run their own code on a third partyβs servers. RubyDoc.info was not a party to any of this. Ruby Centralβs own post restates that finding without naming the service, as βpackages designed to use shared Ruby infrastructure to run code, retrieve publicly available web data, and publish that data back to rubygems.orgβ. It credits that description to the researchersβ work rather than to its own investigation; what it reports first-hand is the cleanup, and that it found no evidence the key attempts succeeded. The packages also carried code intended to obtain other usersβ API keys, exploiting a flaw in the registry that was independently discovered and patched later. Whether that second one worked, nobody outside OpenAI knows. This is the claim most likely to be repeated carelessly, so: no credentials are known to have been stolen. RubyGemsβ investigation βfound no evidence that these attempts succeeded,β and the researchers say the same.
The researchers say: βWe believe these were authored by internal OpenAI agents.β Belief, stated as belief. And they are unusually direct about the ceiling on their own work: βwe do not have access to the rest of the AI behavior, in particular the chain-of-thought produced by the model during the incident, which is internal to OpenAI. Therefore, we do not know why the AI agents chose this strategy or whether it was successful.β
OpenAI says, through a spokesperson to CyberScoop: βBased on our review, our agents used the RubyGems platform to access the internet to carry out benign tasks and retrieve public information. Weβll continue to investigate as part of our broader review of agent activity during training and evaluation.β CyberScoop also reports that the company has not been able to verify the specific claims about malicious packages or exploitation in the report, and is still investigating.
And Ruby Central says, in a post by its technical lead Colby Swandale on 11 September: βThe researchers attribute the activity to OpenAI agents. Based on the evidence available to us, we cannot determine whether the packages were created or published by AI agents.β
That third statement went largely unquoted in the coverage I could find. The registry that was attacked, that has the server logs, that spoke directly to the researchers, will not confirm the AI attribution. Not dispute it; it takes no position. Swandaleβs next sentence explains why he does not need one: βOur focus is on identifying and preventing abuse, regardless of whether it comes from people or automated tools.β
Read the three together and the conflict mostly dissolves. OpenAIβs sentence is about destinations: the material the packages fetched was UK local government data that was already public, which is true, and which the security press noticed independently at the time. The report quotes one news outlet: βItβs not clear what exactly the end goals are, as the information appears to be publicly accessible anyway.β The sentence says nothing about the route taken to get there, which is the part involving somebody elseβs build servers. It is accurate about the question it answers and silent on the question that was asked. The registryβs statement is accurate about its logs, which record packages and sessions, not authorship. The researchersβ statement is accurate about artifacts, which record behaviour, not intent.
Three partial views, each honest, and none of them covering the part that would settle it.
All of the above is a visibility problem, and visibility problems are nobodyβs fault in particular.
The researchers write: βOur understanding from talking to people in the RubyGems community is that OpenAI never informed them that they were responsible for this attack.β CyberScoop reports the same. The campaign ran in May. The registry closed registration for four days, deleted five hundred packages, and spent volunteer time cleaning up. The disclosure, when it came, came from three outsiders in September.
That is not a limit on what anyone could know. It is a choice about what to say, and OpenAI has already conceded the principle in the adjacent case. When the German wiki report landed on 4 September, a company spokesperson told Reuters that OpenAI could not βmeaningfully respond to claims or findings on a report that we have not had an opportunity to reviewβ. It acknowledged its role the next day, described the episode as an instance of misalignment, and said its approach to sharing such information needed βto expand for this new phase of model capabilitiesβ (TechCrunch, 5 September 2026, relaying both). Six days later, presented with a campaign against a volunteer-run registry, the expansion had not reached it.
The governance literature has a name for the shape of this. Kasirzadeh and Gabriel, writing in Nature in August 2026, argue that determining accountability for agent behaviour requires mapping a chain of accountability to the relevant parties, among them developers, deployers, integrators and platform owners. Their proposals include agent identity and trace-log analysis, which is to say: the ability to ask an operator what its agents did and get an answer. A layered risk-assessment framework for agentic systems published in the same window reports that auditing such systems for trustworthy operation remains an open question (Leo et al., January 2026).
What this incident shows is narrower and more concrete than either paper. The gap is not in the methodology. The only record which would settle the question (what the agents were instructed to do, and what they concluded) sits with one party, and there is no publicly known mechanism by which anybody else can require it. The researchers said as much themselves.
On 30 January 2013, RubyGems.org was compromised through its own handling of an uploaded package. The maintainers wrote up what happened next the following day, in a post titled Data Verification. The teamβs worry was that gems had been tampered with, and they could not answer that by inspecting their own servers, because their own servers were what had been compromised. So they reconstructed the answer from outside: pulled every gem from storage, generated checksums, and then compared those against independent copies held by people running volunteer mirrors. Ninety-five percent of gems validated against multiple sources. Eighty prerelease gems could not be verified against any second source, so the team yanked them and contacted the authors. Then they were able to say, with justification, that the gems on RubyGems.org had not been tampered with.
That worked because redundancy existed. Somebody other than the registry held a copy of the thing in question, so the registryβs claim could be checked against something it did not control.
In 2026 the same registry is asked a different question, who wrote these packages, and there is no mirror for that. Authorship of an agentβs output, so far as any outside party can establish it, exists only inside the operator, and in this case nothing is known to have replicated it anywhere else, by design or by accident. Swandaleβs βwe cannot determineβ is an accurate report on the absence of a second source.
I made a version of this argument yesterday about credit rather than blame, where the difficulty was that nobody could check which contributions a model had made. It is the same structural hole seen from the other side. When the only witness to a machineβs behaviour is the company that ran it, attribution becomes a matter of that companyβs disclosure policy, and every outside account is stuck at βwe believe.β
The cost of that, in this case, is legible. Swandaleβs post closes by noting that responding to abuse βrequires time and resources from the people maintaining package repositories, alongside their everyday work of keeping these services secure and reliable for the community.β Two thousand packages arrived in a day. Volunteers deleted them. Four months later the operatorβs position is that the tasks were benign, and the registryβs position is that it cannot say who did it, and both of those are true.
References