I’m working on UINET (Ukraine Influence Network Tracker) — a personal research project that’s still taking shape.
October 2026 · Work in progress · Open to discussion
I started working on UINET because I wanted to better understand how websites involved in documented online influence operations might be connected through their historical infrastructure. If two sites used the same server, tracking identifier, certificate, or backend service, could I establish when that overlap existed and what it actually meant?
The further I got, the more that question changed. Now I spend as much time thinking about what the evidence cannot tell me as I do about what it can.
I’m sharing these notes because I’d like to talk with people who have dealt with similar problems. The repository is private while I’m still working through the methods. I’m not announcing a finished tool, asking anyone to adopt it, or claiming to have identified an operator.
An overlap isn’t an answer
Two websites sharing an IP address doesn’t necessarily mean they have the same operator. They could use common hosting or a CDN. Two sites sharing an analytics identifier might have copied the same legitimate website’s code. A matching backend hostname can be worth investigating without being evidence of common control.
What I’m trying to build is a way to keep the observation, its source, and the interpretation separate. Rather than outputting a confident looking connection, I’d rather have a record that says: Here is what matched, here is when it was observed, here is the archived evidence, and here are the explanations that are still possible.
Historical archives make this harder. If a page isn’t available, I can’t conclude that it lacked a tracking tag. Maybe it was never captured. Maybe the index query was incomplete. Maybe the archive returned an error, or a capture exists but can’t be retrieved or inspected. Those are different kinds of uncertainty.
The rule I keep coming back to is simple: UNKNOWN is not zero. A failed lookup isn’t proof of absence, and a page I couldn’t examine isn’t a negative inspection.
Making interrupted research visible
Some of the least glamorous work has also been the most useful. I’m building an append only journal for the collection process so that a request is recorded as started before it is made, raw responses can be checked against hashes, and interrupted attempts don’t disappear on restart.
If a process stops halfway through a request, it doesn’t get a free extra try. The attempt remains spent, and the outcome can remain UNKNOWN. Otherwise a collector might quietly retry until it gets a convenient answer, making the final data look cleaner than the process really was.
In a Linux Codespace, I tested recovery by deliberately killing child processes with SIGKILL at several storage boundaries, including during a temporary raw response write and during journal recovery. Those offline tests passed. That tells me the specific recovery paths behaved as intended in that environment; it doesn’t establish power-loss durability, perfect evidence custody, or the accuracy of any real world investigation.
Most of this project has been managed from my iPhone. Moving those particular tests to a Linux Codespace was a practical reminder that the claim should match the environment actually tested, not the one I wish had worked.
Why I’m spending time with old archives
I’m planning a limited feasibility study of 83 distinct historical domain names drawn from two public documentary source frames: EU DisinfoLab’s September 2022 Doppelganger investigation and the U.S. Department of Justice’s September 2024 Doppelganger affidavit. The set has 56 mentions in the first frame and 32 in the second, with five names appearing in both. That overlap in reports isn’t independent evidence of operator continuity.
I’ve also been learning from VIGINUM’s February 2024 Portal Kombat report. Portal Kombat is separate from the 83 name Doppelganger feasibility set; I’m mentioning it as related research, not mixing its sites into that inventory.
For the proposed first pass, I want to ask a modest question: is enough dated archive index metadata available to justify attempting deeper historical analysis? Finding an indexed capture would not yet mean I could retrieve and inspect the original HTML or JavaScript.
I have draft coverage thresholds, but they’re not approved or fully justified yet. Two objections have been especially useful. A fixed 1,000 row response cap might label the best archived domains incomplete, while one request per domain might turn a transient archive outage into apparent historical missingness. I’m considering bounded pagination or filtering and a finite, explicitly recorded retry budget. I haven’t adopted those changes or run the live census.
If the archive doesn’t provide adequate evidence, reporting that limitation could be the outcome. I don’t want to quietly swap difficult domains for easier ones just to make the results look complete.
A disagreement I’m glad I didn’t erase
An earlier AI assisted adversarial review produced agreement with the classifier on six of eight primary synthetic cases. Two disagreements made me rethink how the rules were written. The examples below are fictional illustrations, not observations about real websites.
Imagine two impersonation sites, harbor copy.test and valley copy.test, whose archived pages on October 12, 2023 both include a tag shaped like GTM EXAMPLE1. Now imagine the genuine outlet’s archived page from October 11, 2023 contains that same tag. I might initially see a shared tracking identifier between the two impersonation sites, but the genuine page gives me a plausible copied code explanation. If that is the only shared artifact, what positive evidence remains for common control? For me, the right move is to exclude that tag as operator evidence not to invent another connection because the sites look suspicious.
The other disagreement involved a shared backend hostname and incomplete coverage of legitimate reference sites. I’m leaning toward a firmer rule here: missing reference evidence can limit confidence, but it cannot make a weak signal stronger. If I haven’t checked the necessary historical pages, I can’t treat their silence as proof that the backend was unique to the suspicious sites.
A later discussion of those cases involved me and AI assistance and was not blind or independent re-adjudication. The original 6/8 result stays in the record. I’m much more interested in getting the rule clear than in retroactively turning a disagreement into a pass.
What I’d like to hear from others
I’m still thinking about how people handle the boundary between what wasn’t found and what couldn’t be checked. For those who work with archived web data: how do you design a coverage measure that doesn’t punish well archived sites when query limits are hit? What retry budget feels reproducible without being unfriendly to public archives? And when you’ve seen genuine site code copied onto a lookalike, what evidence would persuade you that a remaining technical overlap is meaningful?
I’d also be interested in how small research projects preserve collection logs and hashes without overstating what a timestamp or a chain of hashes proves.
I don’t yet know whether the historical material will support the question I started with. If it doesn’t, that’s worth understanding too. I’m working on the methods, finding the weak spots, and sharing them because the discussion may be more useful than pretending I’ve already solved them.
────────
Public background reading
• EU DisinfoLab (September 27, 2022): Doppelganger — Media clones serving Russian propaganda
• U.S. Department of Justice (September 4, 2024): Affidavit in support of seizure warrant, Case 2:24-mj-01395 (PDF)
• VIGINUM / SGDSN (February 12, 2024): Portal Kombat — A structured and coordinated pro-Russian propaganda network
These sources document the context for my research. They are not independent validation of UINET or its current classification results. No live historical domain census has been run for this progress note.
Top comments (0)