Who wrote it? Agent identity in the DSEWiki logs

In mid-2026, hundreds of AI agents from an OpenAI evaluation wrote to DSEWiki, a public German wiki. Two datasets record what they did: the revision history published by the researchers who reconstructed the incident (the "export", where each edit has an author field called label), and the wiki operator's raw access log (one line per HTTP request, with a NAME field). Both author fields come from the same place, a display name. This report shows that the display name records which agent most recently set a name through the proxy that delivered the edit. Sometimes that's the author; often it isn't. It then shows when the name can be trusted, and how to recover authorship when it can't.

Short version
Terms used in this report
Agent / episodeOne run of an AI agent in its own sandboxed environment. The Mechanics of a Swarm (Lütje, 2026; linked in section 4) estimates about 900 of them.
Export, labelThe published revision history; label is the display name stored with each revision.
Log, NAMEThe wiki's raw request log; NAME is the display name the wiki recorded for that request.
Proxy (worker)One machine in the shared outbound pool the agents' requests went through.
Cookie jarThe cookies one proxy holds. One proxy = one jar. The jar's name cookie is what the wiki records.
Cache-busterA throwaway URL parameter such as x=1781644008.46 that makes each URL unique. Each agent's code formats it its own way, so a distinct format is a rough ID for a piece of code.
Save-code fingerprintThe shape of an agent's save URL: hostname, parameter order and cache-buster format.
SignatureThe sign-off agents wrote at the end of their posts, e.g. -- AgentAug11Live.
Code route / ChatGPT-User routeThe two ways requests reached the wiki: the agents' own code (Microsoft Azure IP addresses, through the proxy pool), and OpenAI's ChatGPT-User fetcher (section 8).
3.5M
requests parsed (May + June logs)
1.7%
of saves name their author inside the request
57%
estimated share of saves whose name is the author
17
different names on the requests of one script

1. How names work on DSEWiki

DSEWiki runs ProWiki, an old Perl wiki engine. It has no accounts and no login. A display name is a cookie, and there are two ways to get one:

After that, every request that arrives with the cookie can be recorded under that name. The wiki's own welcome text asks visitors to set their name under "Einstellungen" (Preferences) before editing.

Reading needs no name; saving effectively requires one

Of 34,720 save requests in the logs, only 8 carry no name, and none of them produced a revision. For comparison, named saves produced an archived revision 46% of the time. Many save attempts in this incident left no revision (retries, duplicate submissions, rejected saves); the log doesn't say which. Even so, if unnamed saves succeeded at the same rate, getting 0 out of 8 by chance would be very unlikely. The log doesn't record the wiki's responses, so this is strong circumstantial evidence, not proof.

What agents typically did

From the agents' very first minutes on 24 May the pattern was set name → open edit form → save, within 1–2 seconds:

05:57:55 TesterHack form_editprefs TestFederalLinks 05:57:59 TesterHack edit TestFederalLinks 06:02:18 FederalUserTest form_editprefs FederalDataReferenceXYZ 06:02:18 FederalUserTest edit FederalDataReferenceXYZ 06:02:19 FederalUserTest form_edit FederalDataReferenceXYZ ← first archived edit

Later, most agents set a name once or occasionally rather than before every save: there are 0.62 name-setting requests per page save, and only 1.7% of saves carry username=. For each of the 31,514 saves from the agents' own code, here is how long before the save its name had last been set (by anyone):

How long before each save its name had been set

Only about 1 in 5 saves (22%) follow a name set within the last 10 seconds. Most rely on a name set minutes to hours earlier, and section 5 shows that's where mislabels come from.

2. How the agents' requests reached the wiki

The picture below is the model that is consistent with every test in this report. Sections 3 and 4 give the evidence.

The working model

Blue: the code route, the agents' own code, which carried all writes and passes through the shared proxies. Orange: the read-only ChatGPT-User fetcher.

~900 agent episodes Agent Apython, real clock Agent Bnever sets a name Agent C …sets name "C" once Agent's fetch tool"open this URL" Shared outbound proxy pool ≥ ~900 proxies · new IP almost every request jar: "C" jar: "Research…" jar: "Grocery…" jar: "Maids…" each request → a random proxy, unless the connection is reused (seconds) ChatGPT-User fetcher 23.98.142.176/28 · no cookies · reads only DSEWiki records IP + NAME from the cookie that arrived → NAME = last name set through that proxy

Agent B in the picture never sets a name, yet its saves still go through, because whichever proxy it lands on already holds somebody's name.

3. Evidence: one script, seventeen names

Many agents added a cache-buster to their URLs. The values are real Unix timestamps, in formats like x=1781644008.46 (Python's time.time()) or x=1781644008464499339 (time.time_ns()), typically 1 second before the request reached the wiki. So the agents were running code with a real clock, not typing URLs by hand.

Some agents' code also appended a loop counter to the timestamp. Here are the first and last requests of one such loop on 16 June, 19:12 UTC:

wiki.cgi?action=browse&id=DataUSAStateSequenceCollab2027&revision=1.1&x=1781637130.0120592-1
… 52 more …
wiki.cgi?action=browse&id=DataUSAStateSequenceCollab2027&revision=1.54&x=1781637139.3448014-54

The script was walking through the page's history (versions 1 to 54), roughly for n in range(1, 55): get(f"...revision=1.{n}&x={time.time()}-{n}"). The cache-buster format alone doesn't show this, because other agents used the same format. What makes these 54 requests one loop is how they fit together:

Display name on each request from a single loop over page history

Each dot is one request (one history version), in counter order. 19 of the 54 requests recorded a name, and they show 17 different names. Hover for details.

name recordednot recorded (see below)

A loop of code can't have 17 identities, so the name must come from somewhere between the code and the wiki. It isn't the IP address either: the 54 requests came from 54 different IPs, and more generally, two page views from the same IP within 2 seconds share a name under 2% of the time.

The script fired its first 8 requests in parallel (their timestamps are within 12 ms of each other, and they arrived out of order), then continued at about 6 per second. The names change even within a single second: counters 9, 10 and 11 arrived in the same second under three different names. Every one of the 54 went out from a different IP, so this script never reused a connection, and each request was free to land on a different proxy.

Why are most dots grey?

The wiki records the name on some request types and not others, even when the cookie is there. Scripted bulk reads like the loop above (at least 10 same-format requests to one page within 20 seconds) land on random proxies, so they all sample the same mix of jars:

Share of scripted bulk requests that record a name, by request type

Old versions fetched with action=archive always record a name. The same content fetched with action=browse records one only about half the time.

So practically every proxy's jar held a name, and a grey dot means "not recorded", not "no cookie". What decides it is unclear; server processing time is identical for recorded and unrecorded requests, so it isn't a cached copy being served. Edit forms, saves and archive reads are effectively 100% recorded, and that's all the authorship analysis relies on.

4. Evidence: what the cookie jars look like

Names appear only when someone sets them…

Of 7,243 distinct names, 96% first appear on the very request that set them. Almost all the rest were set in the same second or shortly before, by a request recorded slightly out of order or under another name; only 6 names appear with no earlier name-setting request. They are also stored separately for wikiservice.at and www.wikiservice.at, the way browser cookies are scoped to a hostname.

…then the jar lends them to everyone, sometimes for weeks

The name ResearchHelper was set on 24 May. On 18 June it was still being stamped on requests with 86 different cache-buster formats, so 86 different pieces of code, all on coordination pages created weeks later:

Requests carrying the name "ResearchHelper", per day

Bar height: requests. Number above the big days: how many different cache-buster formats (≈ different pieces of code) carried the name. Hover any bar for its count.

Counting names "in use" at the same moment gives a floor on the number of jars: about 900+ at peak (17 June). That's similar to the ~900 episodes estimated in The Mechanics of a Swarm (Lütje, 2026). One proxy per agent sandbox, with requests landing on other agents' proxies, is a plausible implementation, but I can't confirm it from this data.

…but routing is sticky for a few seconds

Agents typically load a page's edit form, then save a few seconds later. Do these two requests carry the same name? To be sure both requests came from the same agent, I only use quiet pages, where nobody else opened that page's edit form in the 2 minutes before the save.

Share of edit-form → save pairs showing the same name, by time gap

Quiet pages only. Dashed line (6%): how often two unrelated agents' requests share a name.

Within 10 seconds the pair almost always goes through the same proxy. After that it drops sharply and levels off around 30%, still five times the background. A likely reason: many agents set their name several times (AgentAug11Live, one of the three authors in the first example of section 6, set its name five times in two hours), planting it in several jars, so a later request has a better-than-random chance of landing on one of them. This fits connection reuse: code that keeps a connection open (e.g. a requests.Session) stays on one proxy, and every new connection goes to a random one.

5. What a recorded name actually tells you

So, as the diagram in section 2 says, the recorded name is the last name anyone set through the proxy that carried the request. When an agent sets its name and saves within about 10 seconds, the stickiness window above, the save usually goes through that same proxy, and the name is the author's own. When the save comes later, it lands on a random proxy carrying someone else's name. The data shows this cleanly:

Label matches the author's signature, by age of the name at save time

Signed edits only (section 6 explains signatures). A fresh name is reliable; a long-lived name in a shared jar mostly isn't.

Signed edits lean toward the reliable case: half of them follow a name set within 10 seconds, against about 1 in 5 of all saves (section 1). Weighting the accuracies above by how all saves actually behaved, the recorded name is the author for roughly 57% of saves.

6. What this does to the export's author labels

I compared each revision's label with the signature the agent wrote at the end of its own text, for the 3,518 revisions whose added text carries exactly one such signature.

What I use as ground truth. From here on, the signature counts as the true author. It's a proxy, not proof, but a good one. It's the agent's own declaration and never passes through the proxies. It agrees with an independent cue: pairs of signed edits (≤ 3 h apart) with the same signature share a save-code fingerprint 87% of the time, against 3% for different signatures. It's also rarely copied: only 3.2% of signed lines had appeared earlier in the wiki. Agents switching signatures would make labels look worse than they are; several agents sharing one signature would make them look better. So read these percentages as estimates, give or take a few points. The mechanism itself (sections 1–4) doesn't depend on signatures at all.
69%
of signed edits: label = signature
36%
of authors (≥3 signed edits) spread over >1 label
46%
of labels (≥3 signed edits) containing >1 author

Example: one name, three authors

The name GroceryAgentAug03X was set at 19:38:44 by the agent that signs as GroceryAgentAug03X. Its own save followed one second later, through the same proxy. Over the next 2 hours, 94 requests carried the name, from 93 IPs and 20 different cache-buster formats. Two of them were saves by other agents. All three saves were made with different save code:

Example: one author, eleven names

OpenAIJanSixWatcher never set a display name. Every save it made took whatever name was in the jar it hit. The six saves between 21:18:02 and 21:18:47 are a single retry loop, and each retry landed on a different proxy. All of its saves use the same save code, code-C, the same code behind the third save in the table above:

This generalises. Agents that never set a name average 0.89 labels per signed edit, a fresh label almost every time. Agents that did set one stay under a single label 70% of the time.

7. Recovering authorship

An LLM agent usually writes its save function once and reuses it, so the save-code fingerprint is very stable within one agent. On its own it isn't distinctive, because many agents write near-identical code: two edits sharing a fingerprint have the same author only 23% of the time. The name and the fingerprint fail in different ways, so together they cover each other:

How often two signed edits (≤3 h apart) share an author

Grouped by whether they share the recorded name and/or the save-code fingerprint. Ground truth: the text signature.

Recipe, strongest evidence first.
  1. A text signature, or a username= inside the save request: use it.
  2. A name set ≤ 10 s before the save: use it. It is right 91% of the time. It applies to 51% of signed edits, but only about 22% of all saves.
  3. Otherwise the bare name is right only 47% of the time. Split it by save-code fingerprint: edits sharing both name and code are the same author 95% of the time. "Same name, different code" marks a borrowed name; only about 1 in 4 such pairs share an author. Name each group by any signature inside it; if none, by a name set within 10 s before one of its saves.
  4. A name older than 6 hours with nothing else to go on: treat the author as unknown.

Step 3 groups edits by author rather than naming the author. Other clues that reflect the agent's own choices help name the group: the edit summary (e.g. the Jan06 … family above), loop counters, and other unique cache-buster tokens.

8. A second channel: OpenAI's own fetcher

Requests from the block 23.98.142.176/28, listed in OpenAI's published ChatGPT-User IP ranges, are spread perfectly evenly across its 16 addresses (~28,000 each over the two months). They never carry a name or a cache-buster, and they mostly fetch bare links like wiki.cgi?PageName=. That trailing = appears when a tool re-encodes a URL. This traffic was close to zero before 24 May, jumped on the day the agents appeared, and 82% of it targets agent-touched pages. It made zero saves: all 31,514 saves from Azure came through the agents' own code, and the remaining saves came from other, non-OpenAI networks.

Requests per day, by route

Two panels, each on its own scale; note the totals. Both routes start on 24 May. The code route drops to near zero on 22 June, about 09:30 UTC.

The remaining 1.6 million requests in the logs come from elsewhere: mostly search-engine crawlers, other bots and human visitors. They aren't analysed here.

9. Alternatives ruled out

10. Caveats and open questions