Although I never submitted to it, I made several appearances in the now-defunct quote database on bash.org (QDB). I m dealing with a broken keyboard now, and went to dig hard to find this classic in the Wayback machine. I thought I would put it back on the web:
<mako> my letter "eye" stopped worng
<luca> k, too?
<mako> yeah
<luca> sounds like a mountain dew spill
<mako> and comma
<mako> those three
<mako> ths s horrble
<luca> tme for a new eyboard
<luca> 've successfully taen my eyboard apart and fxed t by cleanng t wth alcohol
<mako> stop mang fun of me
<mako> ths s a laptop!
Discovery
Legacy cloud templates often lack the partitioning and bootloader
binaries required for UEFI Secure Boot. Attempting to switch such a VM
to OVMF in Proxmox results in not a bootable disk. We discovered that
a surgical promotion is possible by manipulating the block device and
EFI variables from the hypervisor.
The Problem
Protective MBR Flags: Legacy installers often set
the pmbr_boot flag on the GPT s protective MBR. Strict UEFI
implementations (OVMF) will ignore the GPT if this flag is present.
Missing ESP: Cloud images often lack a FAT32 EFI
System Partition (ESP).
Variable Store: A fresh Proxmox efidisk0 is empty and lacks both the trust certificates
(PK/KEK/db) and the BootOrder entries required for an automated
boot.
The Promotion Rule
To upgrade a SeaBIOS VM to Secure Boot without a full OS reinstall:
1. Surgical Partitioning: Map the disk on the host and
add a FAT32 partition (Type EF00). Clear the pmbr_boot flag from the MBR. 2. Binary
Preparation: Boot the VM in SeaBIOS mode to install shim and grub-efi packages. Use grub2-mkconfig to populate the new ESP. 3. Trust
Injection: Use the virt-fw-vars utility on the
hypervisor to programmatically enroll the Red Hat/Microsoft CA keys and
any custom certificates (e.g., FreeIPA CA) into the VM s efidisk. 4. Boot Pinning: Explicitly set
the UEFI BootOrder to point to the shimx64.efi
path via virt-fw-vars --append-boot-filepath.
Solution (Example Command
Sequence)
On the Proxmox Host (root):
# Map and Clean MBRDEV=$(rbd map pool/disk)parted-s$DEV disk_set pmbr_boot off# Inject Trust and Boot Path (VM must be stopped)virt-fw-vars--inplace /dev/rbd/mapped_efidisk \--enroll-redhat\--add-db<GUID> /path/to/ipa-ca.crt \--append-boot-filepath'\EFI\centos\shimx64.efi'\--sb
This workflow enables high-integrity Secure Boot environments using
existing SeaBIOS infrastructure templates.
Code Blue Emergency (annoying em-dash in original title) is the
seventh book of James White's Sector General science fiction series about
a vast multi-species hospital station. While there are some references to
(and spoilers for) earlier books in the series, you don't have to remember
the previous books to read this one. I had no trouble despite a nine-year
gap.
I read this as part of the Orb General Practice omnibus, which
collects this novel and The Genocidal Healer.
Cha Thrat is a Sommaradvan warrior-surgeon, member of a newly-discovered
species that is beginning the process of contact with the Federation. She
saved a Monitor corps human after an accident on her world, performing
some some highly competent surgery on a species she had never seen before.
That plus her somewhat outcast status on her own world due to her very
traditional attitude towards medical ethics led Sector General to extend
an offer of medical internship, and led her to leap into the unknown by
accepting. This may have been a mistake; there is a great deal that Sector
General does not understand about Sommaradvan medical ethics.
This series entry is another proper (if somewhat episodic) novel and the
first book of the series that doesn't primarily focus on Conway. He makes
an appearance in his new role as Diagnostician, but only as a supporting
character. Code Blue Emergency is told in the tight third-person
perspective of Cha Thrat, an alien who finds many things about Sector
General baffling, confusing, and ethically troubling (and who therefore
provides a good reader surrogate for reintroducing the basics of how the
hospital works).
Using an alien viewpoint is a more sophisticated narrative technique than
White has used previously. I'm glad he tried it, and it mostly works,
although I have some complaints. Cha Thrat comes from the middle caste of
a strictly hierarchical society of three castes, but is also immensely
stubborn and used to a medical system in which doctors take sole
responsibility for their patients. This creates a lot of cultural
conflicts, and I do enjoy science fiction where the human attitudes are
portrayed as the strange ones, but the cultural analysis offered by this
novel is not very deep.
The pattern of this book is for Cha Thrat to stumble into a successful
approach to a problem while being either oblivious to or hostile to the
normal hierarchical structure expected of medical trainees. This is
believable as far as it goes. She is a skilled and intelligent doctor with
some good instincts and a strong commitment to patient care, but is also
culturally inclined to not ask for help. It makes sense for that to be a
serious problem in a hospital. Unfortunately, no one says this directly.
Sector General staff get quite upset in ways that seem more territorial
than oriented towards patient safety, no one directly explains to Cha
Thrat why following a process is important or shows examples of what could
go wrong, and plot armor means that her mistakes usually have positive
outcomes. One can extrapolate the reasons why she is not a good medical
student, but the reader is forced to do the extrapolation.
This is the sort of book where the narration makes clear there are
unresolved cultural clashes that are going to cause problems but hides the
details. To Cha Thrat, her perspective is so obvious she never bothers to
explain it to the reader, so the specifics come as a surprise. As with the
alien perspective, I've seen this technique used with more subtlety and
sophistication in other books, but White's version mostly works. Cha Thrat
is a sympathetic protagonist because she is truly trying to take the most
ethical and empathetic action in every situation and is clearly competent.
Most of my frustration as a reader, ironically, lands on the other Sector
General doctors who seem to make little to no effort to understand her
perspective when she fails to conform to their expectations. This is
believable in the abstract, but the whole point of Sector General is that
they're supposed to be wiser about interspecies difference than this.
Also, sometimes their reactions just seem petty. Cha Thrat has a very
hierarchical concept of medicine that matches the social classes of her
culture. For her, the highest tier of doctor are wizards who treat rulers,
because the work of rulers is mostly mental and intellectual and therefore
the diseases of rulers are treated with magic spells performed with words
to reshape their thinking rather than surgery on their bodies. O'Mara and
the other Sector General psychologists take great offense at this,
muttering about being called witch doctors, which I found completely
absurd. This is a comprehensible, if odd, description of psychology from a
wholly alien species. Surely one's first reaction should be that words
like "wizard" or "magic" are translation errors. Don't get offended; look
to see if the underlying substance matches, which it clearly does.
Apart from cultural and psychological clashes, Code Blue Emergency
has the standard episodic Sector General structure of interesting medical
mysteries that require lateral thinking. I find this sort of puzzle story
satisfying, particularly given the firm belief of every character in an
essentially pacifist and empathetic approach to even the most alien of
creatures. This determined non-violence is one of the more interesting
things about this series, and it continues here.
White does tend towards both biological and gender essentialism for
everyone other than the protagonist and main supporting characters, but he
seemed to be walking back some of the more outrageous limitations on women
that appeared in previous books. There is still some nonsense in here
about how females of any species can't be Diagnosticians, but then Cha
Thrat, who is female, seems to violate the justification for that rule
over the course of this novel (sadly without comment). Perhaps he's
setting up for proving Sector General wrong about this prejudice.
I picked this up after reading Elizabeth Bear's Machine, which is essentially a (better written) Sector General
novel that got me in the mood for reading more. I wouldn't give Code
Blue Emergency any awards, but it delivered exactly what I was looking
for. This series is not as deep or well-written as some more recent SF,
but it is reliably itself and reliably entertaining. There are worse
things in a series. Recommended if you're in the mood for alien ER
in space.
The omnibus edition that I read has an introduction to both novels by John
Clute. It does add some interesting insights, but (as is somewhat typical
for Clute) it also spoils parts of both books. You may want to read it
after you read the novels.
Followed by The Genocidal Healer.
Rating: 7 out of 10
At May First we have been carefully planning our
migration of about 1200 lists from mailman2 to mailman3 for almost six months
now. We did a lot of user communications, had several months of beta testing
with a handful of lists ported over, and everything was looking good. So we
kicked off the migration!
But, about 15% of the way through I started seeing sqlite lock errors. Wait,
what? I carefully re-configured mailman3 to use postgres, not sqlite. Well,
yes, but apparently that was for the database managing the email list
configuration, not the database powering the django web app, which,
incidentally, also includes hundresds of gigabytes of archives. In other words,
the one we really need in postgres, not sqlite.
Moving from sqlite to postgres
Well that sucks. We immediately stopped the migration to deal with this.
I noticed that the web is full of useful django instructions on how to migrate
your database from one database to antoher. However, if you read the fine
print, those convenient looking dumpdataloaddata workflows are designed
to move the table definitions and a small amount of data. In our case, even
after just 15% of our lists moved, our sqlite database was about 30GB.
I considered some of the hacks to manage memory and try to run this via django,
but eventually decided that pgloader was a more robust
option. This option also allowed me to more easily test things out on a copy of
our sqlite database (made while mailman was turned off). This way I could
migrate and re-migrate the sqlite database over and over without impacting our
live installation until I was satisfied it was all working.
My first decision was to opt out of pgloader s schema creation. I used django s
schema creation tool by:
Turning off mailman3 and mailman3-web and changing the mailman web
configuration to use the new postgresql database.
Running mailman-web migrate
Changing the mailman web configuration back to sqlite and starting
everything again.
Note: I tried just adding new database settings in the mailman web
configuration indexed to new - django has the ability to define different
databases by name, then you can run mailman-web migrate --database new. But,
during the migration, I caught django querying the sqlite database for some
migrations that required referencing existing fields (specifically hyperkitty s
0003_thread_starting_email). I didn t want any of these steps to touch the
live database so I opted for the cleaner approach.
Once I had a clean postgres schema, I dumped it so I could easily return to
this spot.
Next I started working on our pgloader load file. After a lot of trial and
error, I ended with:
The batch, prefetch, workers and concurreny settings are all there to ensure
memory doesn t blow up.
I also discovered that I had to make some changes to the schema before loading
data. Mostly truncating tables that the django migrate command populated to
avoid duplicate key errors:
And also, I had to change a column type. Apparently the mailman import process
allowed an attachment file name that exceeds the limit for postgres, but was
allowed into sqlite:
ALTER TABLE hyperkitty_attachment ALTER COLUMN name TYPE text
When pgloader runs, we still get a lot of warnings from pgloader, which wants
to cast columns differently than django does. These are harmless (I was able to
import the data without a problem).
And there are still a lot of warnings along the lines of:
2026-03-30T14:08:01.691990Z WARNING PostgreSQL warning: constraint hyperkitty_vote_email_id_73a50f4d_fk_hyperkitty_email_id of relation hyperkitty_vote does not exist, skipping
These are harmless as well. They appear because disable triggers disables
foreign key constraints. Without it, we wouldn t be able to load tables that
require values in tables that have not yet been populated.
After all the tweaking, the import of our 30GB sqlite database took about 40
minutes.
Final Steps
I think the reset sequences from pgloader should take care of this, but just in case:
And, just to ensure postgres is optimized, run this in the psql shell:
ANALYZE VERBOSE;
Last thoughts
I understand very well all the decisions the mailman3 devs made in designing
the next version of mailman, and if I was in the same place I may have made
them the same ones. For example, separating the code running the mailing list
from the code managing the archives and the web interface makes perfectly good
sense - many people might want to run just the mailing list part without a web
interface. And building the web interface in django makes a lot of sense as
well - why re-invent the wheel? I m sure a lot of time and effort was saved by
simply using the built in features you get for free with django.
But the unfortunate consequence of these decisions is that sys admins have a
much harder time. Almost everyone wants the email lists along with the web
interface and the archives. But nobody wants two different configuration files
with different syntaxes and logic, not to mention two different command lines
to use for maintenance and configuration with completely different APIs. Trying
to understand how to change a default template or set list defaults requires a
lot of research and usually you have to write a python script to do it.
I have finally come to the conclusion that mailman2 is designed for sys admins,
while mailman3 is designed for developers.
Despite these short comings, I am impressed with the community and their quick
and friendly responses to the questions of a confused sys admin. That might be
more valuable than anything else.
We seem to be entering an AI apocalypse of sorts, they aren t going to kill us or even take our jobs. What they are doing is destroying the Internet commons by filling it with rubbish. This isn t even real AI, just pattern matching and prediction systems, mostly LLMs.
The Problem
Scott Shambaugh s saga of being attacked and defamed by an OpenClaw AI bot is interesting and raises some disturbing possibilities for future online discussion [1]. Imagine what it would be like if everyone who was in any way notable for free software work had 100 such bots going after them.
Dania Dumas wrote an insightful blog post about why OpenClaw is impossible to secure and why it won t go away [2].
Bruce Schneier and Nathan E. Sanders wrote an insightful article about the AI generated text arms race [3] primarily concentrating on situations in which text that was assumed to be written by humans but was actually written in bulk by bots was performing a DOS attack on people who were reviewing it. There are many situations such as book publishing and publishing letters to the editor of newspapers where getting new material from unknown people is an important part of the job but where there are also people making low quality submissions that are almost a DOS attack at the best of times.
Currently the email spam problem continues to get worse and when LLM use increases it will get significantly worse. Email encryption isn t viable [4]. The PGP web of trust never really worked well as it s too difficult for most users.
The amount of AI generated content that s being recommended to users on platforms like YouTube and Facebook is steadily increasing and the amount of LLM generated commentary that purports to be from real people on Twitter and Facebook is also increasing. Here s an informative blog post by Erich Schubert about this [5].
Potential Solutions
Surrender?
One option and possibly the default option is to surrender to this and just let everything we built on the Internet over decades get destroyed. Whether to surrender is a decision that can be made on a per-service basis.
Twitter is pretty much useless anyway, I quit Twitter because Elon deliberately made it suck [6]. In my opinion this is not surrendering to what s being done there, I m just stopping wasting time on it and using better options. I used to have about 300 followers on Twitter and I don t think that many of them would ever choose to stop following me, so I presume that about 1/3 of the people following me have decided to totally quit Twitter and delete their accounts. I also presume that some of the remainder have done the same as me and just kept a mostly inactive account. If Elon suddenly stopped being a stupid asshole it probably wouldn t change anything as the value of the system was connections to others. Some people will consider my abandonment of Twitter as surrender and I accept that it s not an unreasonable opinion. I think that the possibly 100 Twitter followers of mine who deleted their accounts surrendered.
Facebook has been becoming a worse service, it s business model is becoming increasingly exploitative and it s interface is designed to be addictive. It s probably best avoided unless you really need it. The only good thing about Facebook at the moment is that Facebook Marketplace doesn t take a cut on sales and there are some really good deals on computers if you know what to look for. Unfortunately Facebook has a large number of users who are from marginalised communities and have no other alternatives for communication. It would be good to get them migrated to other platforms.
We could just give up on a lot of general communications services and have everyone accept that good content is drowned out by rubbish and have the Internet become divided between people who accept the rubbish and those who cease using large portions of the Internet environment to avoid it.
Using Non Commercial Services
Lemmy is a good FOSS federated alternative to Reddit which also covers some of the uses of Facebook. It needs more users to get critical mass but is still quite usable. A post that might get a dozen comments on Reddit may get 1 comment on Lemmy but that one comment will be a good one. Reddit doesn t appear to be attacked much by LLM generated content at least not yet. Even if the Reddit model proves to be resilient to LLM attack the Lemmy software can be used to replace some things that are done on Facebook,
Mastodon is a good FOSS federated replacement for Twitter, it has a decent user-base including some VIPs. While it is aimed at the Twitter use case it can also cover a significant part of the Facebook use case.
There are some other FOSS social media programs which could take over other parts of the commercial social media environment.
Generally commercially run Internet services will have a financial incentive to allow the problems to get worse so we need to rely on FOSS software, non-commercial implementations, and government services.
Web Search
For a long time Google has had a monopoly on web search, but now they default to including an AI Overview at the start of the results which is sometimes useful but also sometimes very wrong. You can use the search URL https://www.google.com/search?q=%s&udm=web to get google results without rubbish. But I presume that they will break that if it gets too popular.
Searxng is a AGPL licensed metasearch engine that aggregates results from other engines, here s the Searxng source [7] and here s a list of Searxng instances if you want to try one [8].
Even using meta search engines like Searxng won t help if the original data is overloaded with spam, but alleviating the problem is a good temporary measure.
Web of Trust for the Web?
I ve idly considered the possibility of having some sort of rating system for web pages that uses a web of trust so that you can securely use trust ratings of friends of friends etc. But given all the difficulties in using a web of trust for signing GPG key for software developers (the demographic that is most skilled at doing such things) it doesn t seem viable.
Should we surrender the idea of having a usable public web?
In the early days of the web (before Google) it was standard practice to rely on recommendations from other people or from trusted sites to find other sites, that could be considered to be an informal web of trust. We could go back to that sort of usage pattern if Google and many of the big sites get overwhelmed by LLM generated spam.
Wikipedia
I believe that Wikipedia will be at the front lines of this battle. It s model has always included anonymous contributions. Benjamin Mako Hill wrote an interesting blog post about research he did with Kaylea Champion into Wikipedia pages on taboo topics which have a larger portion of contributors choosing to be anonymous than non-taboo pages [9]. Wikipedia also has a long history of being abused for various reasons, one that I witnessed was someone putting false content into Wikipedia pages to immediately cite them in support of their facebook arguments. That sort of thing can be dealt with at human scale but a large scale attack by bots is a different problem to solve. Also with the recent developments in AI developing multiple web sites entirely populated for the purpose of supporting one fake entry in Wikipedia is plausible.
The upside of these attacks that I predict is that they will attract the attention of all the people who have skills related to developing counter-measures. While LLM bots are filling the inboxes of publishers with rubbish and messing up the stackoverflow comments section not a lot of people are bothered, but once the attacks on Wikipedia get serious everyone will take notice.
National AI
Bruce Schneier and Nathan E. Sanders wrote an interesting blog post about nationalised public AI [10]. While that won t directly address this issue it will get the right technology in the hands of people who can use it in the right way.
Conclusion
This is going to be a difficult problem to solve, more difficult than the email spam problem we have been unable to solve after 30 years of working on it.
This is also a very important problem, we are currently in an age where we have access to information that most people couldn t even dream of 30 years ago. We also have disinformation that combines some of the worst aspects of authoritarian regimes throughout history combined with the worst aspects of cult brainwashing. If we lose access to the information but the disinformation remains (or get worse) then the result will be terrible.
I don t have great ideas for solving this. I have outlined some small ideas to mitigate things and I hope that others can expand on them.
Please write comments with any good ideas you have, or even ideas that don t totally suck. A problem this difficult is not going to be solved in a blog comment, but a blog comment might point in the right direction.
I ve decided it s time to tag a v0.1.0 release on my roguelike game project, Stagger. It s more of a small demo than a full game at this point. It is turn-based, and has purely text-based graphics , like the original Rogue.
Here s a screenshot :
A Shadow in Summer is a high fantasy novel, the first of (as the
name implies) a completed four-book series. Daniel Abraham is perhaps
better known as half of the writing pair behind James S.A. Corey, author
of the Expanse series. This was his first
novel.
Otah was the sixth son of a Khai, sent like many of the unwanted later
children of the powerful to learn the secrets of the andat and be trained
as a poet. He learned his lessons well enough to reject the school and its
teachings and walk away.
Amat Kyaan has worked her way up from nothing to become the senior
overseer of the foreign Galtic House Wilsin in the sun-drenched port city
of Saraykeht. Liat is her apprentice, distracted by young love. Maati is a
new apprentice poet, having endured his training and sent to learn from
Heshai how to eventually hold the andat Removing-The-Part-That-Continues,
better known as Seedless. None of them know they will find themselves
entangled in a plot to destroy the poet of Saraykeht and, through him, the
city's most potent economic tool.
A poet in this world is not what we would think of a poet. They are, in
essence, magical slave-drivers who capture the essence of an andat, a
spirit embodying an idea that is coerced into the prison of volition and
obedience by the poet. The andat Seedless, the embodiment of the concept
of removing the spark of life, is central to the economic wealth of
Saraykeht in a way that is startling in its simplicity: Seedless can
remove the seeds from a warehouse full of cotton at a thought. This gives
Saraykeht a massive productivity advantage in the cotton trade.
Seedless is also a powerful potential weapon. What he can do to cotton, he
could as easily do to any other crop, or to people. The Galts are not fond
of the independence and power of Saraykeht, but as long as the city
controls a powerful andat, they do not dare to attack it directly.
Indirectly, though... that's another matter.
This is one of those fantasy novels with meticulous and thoughtful
world-building, careful and evocative prose, and a complex ensemble cast
of interesting characters that the novel then attempts to make utterly
miserable and complicit in their own misery. There should be a name for
this style of writing. It's not tragedy because the ending is not tragic,
precisely. It's not magic realism; the andats are openly magical, which
makes this clearly high fantasy. But Abraham approaches the story from the
type of realist frame that considers the pain and desperation of the
characters to be more interesting than their ability to overcome
challenges.
Amat starts the story as an admirable, sharp-witted expert manager, so her
life is destroyed and she's subjected to sexual violence. Heshai loathes
himself and veers between a tragic figure and a wastrel as the story
systematically undermines opportunities for redemption. Maati is young and
idealistic, so of course every character in the book sets out to crush his
idealism under the weight of unforeseen consequences. There is a sad and
depressing love triangle, because this is exactly the sort of book that
has a sad and depressing love triangle. At the end of the novel, everyone
who survives is older and wiser in the sense that some stories seem to
think wisdom comes from the accumulation of trauma.
I find books like this so immensely frustrating because their merits are
so clear. The world-building is careful and detailed in a way that
includes economic systems, unlike so much fantasy. It is full of small,
intriguing touches, such as the use of posture and gesture to communicate
the emotional valence of one's words. Abraham understands the moral
implications of poets and andats and the story tackles them head-on. The
writing flows beautifully and gave me a strong sense of the city. I wanted
to like this book for the obvious skill that went into it, and sometimes I
even managed.
And yet, it's taken me three months to finish A Shadow in Summer
because I simply do not want to spend this much time around miserable
people. I would get through one or two chapters in a night and then wanted
to read something happy or defiant or heroic, rather than watching
slow-motion train wrecks intermixed with desperate attempts to navigate
stifling layers of immoral systems. It's not that the story lacks a moral
compass. The characters are sincerely trying to make the world a better
place, with some success. It even delivers a happy ending of sorts. But so
much of the journey was watching the lives of the characters fall apart.
I am completely unsurprised that some people loved this book. I'm still
intrigued enough by the world-building that I'm half-tempted to try to
read the sequel even after having to drag myself through this one. I had a
similar reaction to Abraham's The
Dragon's Path, though, so I think Abraham is just not for me. I may get
back to the Expanse at some point, but having to drag myself through both
of his solo novels I've tried, in two different series, probably indicates
an incompatibility between author and reader. That's a shame, given the
quality of the writing.
Followed by A Betrayal in Winter.
Content notes: Sexual and reproductive violence as significant plot
elements.
Rating: 6 out of 10
Note: I have not published blog posts about my academic papers over the past few years. To ensure that my blog contains a more comprehensive record of my published papers and to surface them for folks who missed them, I will periodically (re) publish blog posts about some older published projects. This post draws material from a previously published post by Kaylea Champion on the Community Data Science Blog.
Taboo subjects such as sexuality and mental health are as important to discuss as they are difficult to raise in conversation. Although many people turn to online resources for information on taboo subjects, censorship and low-quality information are common in search results. In two papers I recently published at CSCW both led by Kaylea Champion we presented a series of analyses showing how taboo shapes the process of collaborative knowledge building on English Wikipedia.
The first study is a quantitative analysis showing that articles on taboo subjects are much more popular and are the subject of more vandalism than articles on non-taboo topics. In surprising news, we also found that they were edited more often and were of higher quality!
Short video of Kaylea s presentation of the work given at Wikimania in August 2023.
The first challenge we faced in conducting this work was identifying taboo articles. Kaylea had a brilliant idea for a new computational approach to doing so without relying on our individual intuitions about what qualifies as taboo (something we understood would be highly specific to our own culture, class, etc). Her approach was to make use of an insight from linguistics: people develop euphemisms as ways to talk about taboos (i.e., think about all the euphemisms we ve devised for death, or sex, or menstruation, or mental health).
We used this insight to build a new machine-learning classifier based on English Wiktionary definitions. If a sense of a word was tagged as euphemistic, we treated the words in the definition as indicators of taboo. The end result was a series of words and phrases that most powerfully differentiate taboo from non-taboo. We then did a simple match between those words and phrases and the titles of Wikipedia articles. The topics were taboo enough that we were a little uncomfortable discussing them in our meetings! We built a comparison sample of articles whose titles are words that, like our taboo articles, appear in Wiktionary definitions.
In the first paper, we used this new dataset to test a series of hypotheses about how taboo shapes collaborative production in Wikipedia. Our initial hypotheses were based on the idea that taboo information is often in high demand but that Wikipedians might be reluctant to associate their names (or usernames) with taboo topics. The result, we argued, would be articles that were in high demand but of low quality.
We found that taboo articles are thriving on Wikipedia! In summary, we found that in comparison to non-taboo articles:
Taboo articles are more popular (as expected).
Taboo articles receive more contributions (contrary to expectations).
Taboo articles receive more low-quality contributions (as expected).
Taboo articles are higher quality (contrary to expectations).
Taboo article contributors are more likely to contribute without an account (as expected), and have less experience (as expected), but that accountholders are more likely to make themselves more identifiable by having a user page, disclosing their gender, and making themselves emailable (all three of these are contrary to expectation!).
Image of the estimated qualiy of articles of the four articles in the second mixed-methods paper. Extreme dips reflect periods of frequent vandalism.
Kaylea attempted to understand these somewhat confusing results by designing a fantastic mixed-methods analysis that sought to unpack some of the nuance missing in the quantitative analysis by delving deep into the life histories of four articles on English Wikipedia: two on taboo topics related to women s anatomy (Clitoris and Menstration) and two nontaboo articles chosen for comparison (Cell membrance and Philip Pullman).
Although the findings from the analysis can be difficult to summarize succinctly (as with many qualitative studies), we showed how the taboo example articles success was hard-won amid real challenges and attacks. The paper describes how challenges were overcome through resilient leadership, often provided by a single dedicated individual. The paper provides a template for how taboo can be and frequently is overcome by dedicated Wikipedians in ways that provide useful knowledge resources in real demand.
For more details, visualizations, statistics, and more, we hope you ll take a look at our papers, both linked below.
The full citation for the papers are: (1) Champion, Kaylea, and Benjamin Mako Hill. 2023. Taboo and Collaborative Knowledge Production: Evidence from Wikipedia. Proceedings of the ACM on Human-Computer Interaction 7 (CSCW2): 299:1-299:25. https://doi.org/10.1145/3610090. (2) Champion, Kaylea, and Benjamin Mako Hill. 2024. Life Histories of Taboo Knowledge Artifacts. Proceedings of the ACM: Human-Computer Interaction 8 (CSCW2): 505:1-505:32. https://doi.org/10.1145/3687044.
The
WWW::Mechanize::Chrome Saga: A Comprehensive Narrative of PR #104
This document synthesizes the extensive work performed from March
13th to March 20th, 2026, to harden, stabilize, and refactor the WWW::Mechanize::Chrome library and its test suite. This
effort involved deep dives into asynchronous programming,
platform-specific bug hunting, and strategic architectural
decisions.
Part I:
The Quest for Cross-Platform Stability (March 13 16)
The initial phase of work focused on achieving a green test suite
across a variety of Linux distributions and preparing for a new release.
This involved significant hardening of the library to account for
different browser versions, OS-level security restrictions, and
filesystem differences.
Key Milestones &
Engineering Decisions:
Fedora & RHEL-family Success: A major effort
was undertaken to achieve a 100% pass rate on modern Fedora 43 and
CentOS Stream 10. This required several key engineering decisions to
handle modern browser behavior:
Decision: Implement Asynchronous DOM Serialization
Fallback. Synchronous fallbacks in an async context are
dangerous. To prevent Resource was not cached errors during saveResources, we implemented a fully asynchronous fallback
in _saveResourceTree. By chaining _cached_document with DOM.getOuterHTML
messages, we can reconstruct document content without blocking the event
loop, even if Chromium has evicted the resource from its cache. This
also proved resilient against Fedora s security policies, which often
block file:// access.
Decision: Truncate Filenames for Cross-Platform
Safety. To avoid File name too long errors,
especially on Windows where the MAX_PATH limit is 260
characters, filenameFromUrl was hardened. The filename
truncation was reduced to a more conservative 150
characters, leaving ample headroom for deeply nested CI
temporary directories. Logic was also added to preserve file extensions
during truncation and to sanitize backslashes from URI paths.
Decision: Expand Browser Discovery Paths. To
support RHEL-based systems out-of-the-box, the default_executable_names was expanded to include headless_shell and search paths were updated to include /usr/lib64/chromium-browser/.
Decision: Mitigate Race Conditions with Stabilization Waits
and Resilient Fetching. On fast systems, DOM.documentUpdated events could invalidate nodeIds immediately after navigation, causing XPath queries
to fail with Could not find node with given id . A small stabilization sleep(0.25s) was added after page loads to ensure the DOM
is settled. Furthermore, the asynchronous DOM fetching loop was hardened
to gracefully handle these errors by catching protocol errors and
returning an empty string for any node that was invalidated during
serialization, ensuring the overall process could complete.
Windows Hardening:
Decision: Adopt Platform-Aware Watchdogs. The test
suite s reliance on ualarm was a blocker for Windows, where
it is not implemented. The t::helper::set_watchdog function
was refactored to use standard alarm() (seconds) on Windows
and ualarm (microseconds) on Unix-like systems, enabling
consistent test-level timeout enforcement.
Version 0.77 Release:
Decision: Adopt SOP for Version Synchronization.
The project maintains duplicate version strings across 24+ files. A
Standard Operating Procedure was adopted to use a batch-replacement tool
to update all sub-modules in lib/ and to always run make clean and perl Makefile.PL to ensure META.json and META.yml reflect the new
version. After achieving stability on Linux, the project version was
bumped to 0.77.
Infrastructure & Strategic Work:
The ad2 Windows Server 2025 instance was restored and
optimized, with Active Directory demoted and disk I/O performance
improved.
A strategic proposal for the Heterogeneous Directory
Replication Protocol (HDRP) was drafted and published.
Part II: The
Great Async Refactor (March 17 18)
Despite success on Linux, tests on the slow ad2 Windows
host were still plagued by intermittent, indefinite hangs. This
triggered a fundamental architectural shift to move the library s core
from a mix of synchronous and asynchronous code to a fully non-blocking
internal API.
Key Milestones &
Engineering Decisions:
Decision: Expose a _future API.
Instead of hardcoding timeouts in the library, the core strategy was to
refactor all blocking methods (xpath, field, get, etc.) into thin wrappers around new non-blocking ..._future counterparts. This moved timeout management to
the test harness, allowing for flexible and explicit handling of
stalls.
# Example library implementationsub xpath($self, $query, %options) return$self->xpath_future($query, %options)->get;sub xpath_future($self, $query, %options) # Async implementation using $self->target->send_message(...)
Decision: Centralize Test Hardening in a Helper.
A dedicated test library, t/lib/t/helper.pm, was created to
contain all stabilization logic. Safe wrappers (safe_get, safe_xpath) were implemented there, using Future->wait_any to race asynchronous operations against
a timeout, preventing tests from hanging.
Decision: Refactor Node Attribute Cache.
Investigations into flaky checkbox tests (t/50-tick.t)
revealed that WWW::Mechanize::Chrome::Node was storing
attributes as a flat list ([key, val, key, val]), which was
inefficient for lookups and individual updates. The cache was refactored
to definitively use a HashRef, providing O(1) lookups
and enabling atomic dual-updates where both the browser property (via
JS) and the internal library attribute are synchronized
simultaneously.
Decision: Implement Self-Cancelling Socket
Watchdog. On Windows, traditional watchdog processes often
failed to detect parent termination, leading to 60-second hangs after
successful tests. We implemented a new socket-based watchdog in t::helper that listens on an ephemeral port; the background
process terminates immediately when the parent socket closes,
eliminating these cumulative delays.
Decision: Deep Recursive Refactoring & Form
Selection. To make the API truly non-blocking, the entire
internal call stack had to be refactored. For example, making get_set_value_future non-blocking required first making its
dependency, _field_by_name, asynchronous. This culminated
in refactoring the entire form selection API (form_name, form_id, etc.) to use the new asynchronous _future lookups, which was a key step in mitigating the
Windows deadlocks.
Evaluation Normalization: Implemented a _process_eval_result helper to centralize the parsing of
results from Runtime.evaluate. This ensures consistent
handling of return values and exceptions between synchronous
(eval_in_page) and asynchronous (eval_future)
calls.
Memory Cycle Mitigation: A significant memory
leak was discovered where closures attached to CDP event futures (like
for asynchronous body retrieval) would capture strong references to $self and the $response object, creating a
circular reference. The established rule is to now always use Scalar::Util::weaken on both $self and any
other relevant objects before they are used inside a ->then block that is stored on an object.
Context Propagation (wantarray): A
major regression was discovered where Perl s wantarray
context, which distinguishes between scalar and list context, was lost
inside asynchronous Future->then blocks. This caused
methods like xpath to return incorrect results (e.g., a
count instead of a list of nodes). The solution was to adopt the Async
Context Pattern : capture wantarray in the synchronous
wrapper, pass it as an option to the _future method, and
then use that captured value inside the future s final resolution
block.
Asynchronous Body Retrieval & Robust Content
Fallbacks: Fixed a bug where decoded_content()
would return empty strings by ensuring it awaited a __body_future. This was implemented by storing the
retrieval future directly on the response object
($response-> __body_future ). To make this more robust,
a tiered strategy was implemented: first try to get the content from the
network response, but if that fails (e.g., for about:blank
or due to cache eviction), fall back to a JavaScript XMLSerializer to get the live DOM content.
Signature Hardening: Fixed Too few arguments
errors when using modern Perl signatures with Future->then. Callbacks were updated to use optional
parameters (sub($result = undef) ... ) to gracefully
handle futures that resolve with no value.
XHTML Split-Brain Bug: Resolved a
long-standing Chromium bug (40130141) where content provided via setDocumentContent is parsed differently than content
loaded from a URL. A workaround was implemented: for XHTML documents,
WMC now uses a JavaScript-based XPath evaluation
(document.evaluate) against the live DOM, bypassing the
broken CDP search mechanism.
Derived Architectural Rules
& SOPs:
Rule: Always provide _future variants.
Every library method that interacts with the browser via CDP must have a
non-blocking asynchronous counterpart.
Rule: Centralize stabilization in the test layer.
All timeout and retry logic should reside in the test harness
(t/lib/t/helper.pm), not in the core library.
Rule: Explicitly propagate wantarray
context. Synchronous wrappers must capture the caller s context
and pass it down the Future chain to ensure correct
scalar/list behavior.
Rule: The entire call chain must be asynchronous.
To enable non-blocking timeouts, even a single hidden blocking call in
an otherwise asynchronous method will cause a stall.
SOP: Reduce Library Noise. Diagnostic messages
(warn, note, diag) should be
removed from library code before commits. All such messages should be
converted to use the internal $self->log('debug', ...)
mechanism, ensuring a clean TAP output for CI systems.
Part III: The MutationObserver Saga (March 19)
With most of the library refactored to be asynchronous, one stubborn
test, t/65-is_visible.t, continued to fail with timeouts.
This led to an ambitious, but ultimately unsuccessful, attempt to
replace the wait_until_visible polling logic with a more
modern MutationObserver.
Key Milestones & Challenges:
The Theory: The goal was to replace an inefficient repeat sleep loop with an event-driven MutationObserver in JavaScript that would notify Perl
immediately when an element s visibility changed.
Implementation & Cascade Failure: The
implementation proved incredibly difficult and introduced a series of
new, hard-to-diagnose bugs:
An incorrect function signature for callFunctionOn_future.
A critical unit mismatch, passing seconds from Perl to JavaScript s setTimeout, which expected milliseconds.
A fundamental hang where the MutationObserver s
JavaScript Promise would never resolve, even after the
underlying DOM element changed.
Debugging Maze: Multiple attempts to fix the checkVisibility JavaScript logic inside the observer
callback, including making it more robust by adding DOM tree traversal
and extensive console.log tracing, failed to resolve the
hang. This highlighted the opacity and difficulty of debugging complex,
cross-language asynchronous interactions, especially when dealing with
low-level browser APIs.
Procedural Learning:
Granular Edits
The effort was plagued by procedural missteps in using automated
file-editing tools. Initial attempts to replace large code blocks in a
single operation led to accidental code loss and match failures.
Decision: Adopt Delete, then Add Workflow.
Following forceful user correction, a new SOP was established for all
future modifications:
Isolate: Break the file into small, manageable
chunks (e.g., 250 lines).
Delete: Perform a delete operation by replacing
the old code block with an empty string.
Add: Perform an add operation by inserting the
new code into the empty space.
Verify: Verifying each atomic step before
proceeding. This granular process, while slower, ensured surgical
precision and regained technical control over the large Chrome.pm module.
The consistent failure of the MutationObserver approach
eventually led to the decision to abandon it in favor of stabilizing the
original, more transparent implementation.
Part IV:
Reversion and Final Stabilization (March 20)
After exhausting all reasonable attempts to fix the MutationObserver, a strategic decision was made to revert
to the simpler, more transparent polling implementation and fix it
correctly. This proved to be the correct path to a stable solution.
Key Milestones &
Engineering Decisions:
Decision: Perform Strategic Reversion. The MutationObserver implementation, when integrated via callFunctionOn_future with awaitPromise,
proved fundamentally unstable. Its JavaScript promise would consistently
fail to resolve, causing indefinite hangs. A decision was made to
revert all MutationObserver code from WWW::Mechanize::Chrome.pm and restore the original repeat sleep polling mechanism. A stable,
understandable solution was prioritized over an elegant but broken
one.
Decision: Correct Timeout Delegation in the
Harness. The root cause of the original timeout failure was
identified as a race condition in the t/lib/t/helper.pm
test harness. The safe_wait_until_* wrappers were
implementing their own timeout (via wait_any and sleep_future) that raced against the underlying polling
function s internal timeout. This led to intermittent failures on slow
machines. The helpers were refactored to delegate all timeout
management to the library s polling functions, ensuring a
single, authoritative timer controlled the operation.
Decision: Optimize Polling Performance. At the
user s request, the polling interval was reduced from 300ms to
150ms. This modest performance improvement reduced the
test suite s wallclock execution time by over a second while maintaining
stability.
Decision: Tune Test Watchdogs. The global watchdog
timeout was adjusted to 12 seconds, specifically calculated as 1.5x the
observed real execution time of the optimized test. This provides a
data-driven safety margin for CI.
Part
V: The Last Bug A Platform-Specific Memory Leak (March 20)
With all other tests passing, a single memory leak failure in t/78-memleak.t persisted, but only on the Windows ad2 environment. This required a different approach than
the timeout fixes.
Key Milestones:
The Bug: A strong reference cycle involving the on_dialog event listener was not being broken on Windows,
despite multiple attempts to fix it. Fixes that worked on Linux (such as
calling on_dialog(undef) in DESTROY) were not
sufficient on the Windows host.
The Diagnosis: The issue was determined to be a
deep, platform-specific interaction between Perl s garbage collector,
the IO::Async event loop implementation on Windows, and the Test::Memory::Cycle module. The cycle report was identical
on both platforms, but the cleanup behavior was different.
Failed Attempts: A series of increasingly
aggressive fixes were attempted to break the cycle, including:
Moving the on_dialog(undef) call from close() to DESTROY().
Explicitly deleteing the listener and callback
properties from the object hash in DESTROY.
Swapping between $self->remove_listener and $self->target->unlisten in a mistaken attempt to find
the correct un-registration method.
Pragmatic Solution: After exhausting all reasonable
code-level fixes without a resolution on Windows, the user opted to mark
the failing test as a known issue for that specific platform.
Final Fix: The single failing test in t/78-memleak.t was wrapped in a conditional TODO block that only executes on Windows
(if ($^O =~ /MSWin32/i)), formally acknowledging the bug
without blocking the build. This allows the test suite to pass in CI
environments while flagging the issue for future, deeper
investigation.
Part VI: CI Hardening (March
20)
A final failure in the GitHub Actions CI environment revealed one
last configuration flaw.
Key Milestones:
The Bug: The CI was running prove --nocount --jobs 3 -I local/ -bl xt t directly. This
command was missing the crucial -It/lib include path, which
is necessary for test files to locate the t::helper module.
This resulted in nearly all tests failing with Can't locate t/helper.pm in @INC.
The Investigation: An analysis of Makefile.PL revealed a custom MY::test block
specifically designed to inject the -It/lib flag into the make test command. This confirmed that make test is the correct, canonical way to run the test
suite for this project.
The Fix: The .github/workflows/linux.yml file was modified to replace
the direct prove call with make test in the Run Tests step. This ensures the CI environment runs the
tests in the exact same way as a local developer, with all necessary
include paths correctly configured by the project s build system.
Final Outcome
After this long and arduous journey, the WWW::Mechanize::Chrome test suite is now stable and
passing on all targeted platforms, with known
platform-specific issues clearly documented in the code. The project is
in a vastly more robust and reliable state.
Note: I have not published blog posts about my academic papers over the past few years. To ensure that my blog contains a more comprehensive record of my published papers and to surface these for folks who missed them, I will be periodically (re)publishing blog posts about some older published projects. This post is closely based on a previously published post by Zarine Kharazian on the Community Data Science Blog.For nearly a decade, the Croatian language version of Wikipedia was run by a cabal of far-right nationalists who edited articles in ways that promoted fringe political ideas and involved cases of historical revisionism related to the Usta e regime, a fascist movement that ruled the Nazi puppet state called the Independent State of Croatia during World War II. This cabal seized complete control of the encyclopedia s governance, banned and blocked those who disagreed with them, and operated a network of fake accounts to create the appearance of grassroots support for their policies.
Thankfully, Croatian Wikipedia appears to be an outlier. Though both the Croatian and Serbian language editions have been documented to contain nationalist bias and historical revisionism, Croatian Wikipedia seems unique among Wikipedia editions in the extent to which its governance institutions were captured by a small group of users.
The situation in Croatian Wikipedia was well documented and is now largely fixed, but we still know very little about why it was taken over, while other language editions seem to have rebuffed similar capture attempts. In a paper published in the Proceedings of the ACM: Human-Computer Interaction (CSCW), Zarine Kharazian, Kate Starbird, and I present an interview-based study that provides an explanation for why Croatian was captured while several other editions facing similar contexts and threats fared better.
Short video presentation of the work given at Wikimania in August 2023.
Based on insights from interviews with 15 participants from both the Croatian and Serbian Wikipedia projects and from the broader Wikimedia movement, we arrived at three propositions that, together, help explain why Croatian Wikipedia succumbed to capture while Serbian Wikipedia did not:
Perceived Value as a Target. Is the project worth expending the effort to capture?
Bureaucratic Openness. How easy is it for contributors outside the core founding team to ascend to local governance positions?
Institutional Formalization. To what degree does the project prefer personalistic, informal forms of organization over formal ones?
The conceptual model from our paper, visualizing possible institutional configurations among Wikipedia projects that affect the risk of governance capture.
We found that both Croatian and Serbian Wikipedias were attractive targets for far-right nationalist capture due to their sizable readership and resonance with national identity. However, we also found that the two projects diverged early in their trajectories in how open they remained to new contributors ascending to local governance positions and in the degree to which they privileged informal relationships over formal rules and processes as the project s organizing principles. Ultimately, Croatian s relative lack of bureaucratic openness and rules constraining administrator behavior created a window of opportunity for a motivated contingent of editors to seize control of the governance mechanisms of the project.
Though our empirical setting was Wikipedia, our theoretical model may offer insight into the challenges faced by self-governed online communities more broadly. As interest in decentralized alternatives to Facebook and X (formerly Twitter) grows, communities on these sites will likely face similar threats from motivated actors. Understanding the vulnerabilities inherent in these self-governing systems is crucial to building resilient defenses against threats like disinformation.
For more details on our findings, take a look at the published version of our paper.
Citation for the full paper: Kharazian, Zarine, Kate Starbird, and Benjamin Mako Hill. 2024. Governance Capture in a Self-Governing Community: A Qualitative Comparison of the Croatian, Serbian, Bosnian, and Serbo-Croatian Wikipedias. Proceedings of the ACM on Human-Computer Interaction 8 (CSCW1): 61:1-61:26. https://doi.org/10.1145/3637338.
This blog post and the paper it describes are collaborative work by Zarine Kharazian, Benjamin Mako Hill, and Kate Starbird.
Note: I have not published blog posts about my academic papers over the past few years. To ensure that my blog contains a more comprehensive record of my published papers and to surface these for folks who missed them, I will be periodically (re)publishing blog posts about some older published projects.
It seems natural to think of online communities competing for the time and attention of their participants. Over the last few years, I ve worked with a team of collaborators led by Nathan TeBlunthuis to use mathematical and statistical techniques from ecology to understand these dynamics. What we ve found surprised us: competition between online communities is rare and typically short-lived.
When we started this research, we figured competition would be most likely among communities discussing similar topics. As a first step, we identified clusters of such communities on Reddit. One surprising thing we noticed in our Reddit data was that many of these communities that used similar language also had very high levels of overlap among their users. This was puzzling: why were the same groups of people talking to each other about the same things in different places? And why don t they appear to be in competition with each other for their users time and activity?
We didn t know how to answer this question using quantitative methods. As a result, we recruited and interviewed 20 active participants in clusters of highly related subreddits with overlapping user bases (for example, one cluster was focused on vintage audio).
We found that the answer to the puzzle lay in the fact that the people we talked to were looking for three distinct things from the communities they worked in:
The ability to connect to specific information and narrowly scoped discussions.
The ability to socialize with people who are similar to themselves.
Attention from the largest possible audience.
Critically, we also found that these three things represented a trilemma, and that no single community can meet all three needs. You might find two of the three in a single community, but you could never have all three.
Figure from No Community Can Do Everything: Why People Participate in Similar Online Communities depicts three key benefits that people seek from online communities and how individual communities tend not to optimally provide all three. For example, large communities tend not to afford a tight-knit homophilous community.
The end result is something I recognize in how I engage with online communities on platforms like Reddit. People tend to engage with a portfolio of communities that vary in size, specialization, topical focus, and rules. Compared with any single community, such overlapping systems can provide a wider range of benefits. No community can do everything.
This work was published as a paper at CSCW: TeBlunthuis, Nathan, Charles Kiene, Isabella Brown, Laura (Alia) Levi, Nicole McGinnis, and Benjamin Mako Hill. 2022. No Community Can Do Everything: Why People Participate in Similar Online Communities. Proceedings of the ACM on Human-Computer Interaction 6 (CSCW1): 61:1-61:25. https://doi.org/10.1145/3512908.
This work was supported by the National Science Foundation (awards IIS-1908850, IIS-1910202, and GRFP-2016220885). A full list of acknowledgements is in the paper.
Note: I have not published blog posts about my academic papers over the past few years. To ensure that my blog contains a more comprehensive record of my published papers and to surface these for folks who missed them, I will be periodically (re)publishing blog posts about some older published projects. This post is closely based on a previously published post by Kaylea Champion on the Community Data Science Blog.Many individuals use Tor to reduce their visibility to widespread internet surveillance.
One popular approach to protecting our privacy online is to use the Tor network. Tor protects users from being identified by their IP address, which can be tied to a physical location. However, if you d like to contribute to Wikipedia using Tor, you ll run into a problem. Although most IP addresses can edit without an account, Tor users are blocked from editing.
Tor users attempting to contribute to Wikipedia are shown a screen that informs them that they are not allowed to edit Wikipedia.Other research by my team has shown that Wikipedia s attempt to block Tor is imperfect and that some people have been able to edit despite the ban. As part of this work, we built a dataset of more than 11,000 contributions to Wikipedia via Tor and used quantitative analysis to show that contributions from Tor were of about the same quality as contributions from other new editors and other contributors without accounts. Of course, given the unusual circumstances Tor-based contributors face, we wondered whether a deeper look at the content of their edits might reveal more about their motives and the kinds of contributions they seek to make. Kaylea Champion (then a student, now faculty at UW Bothell) led a qualitative investigation to explore these questions.
Given the challenges of studying anonymity seekers, we designed a novel forensic qualitative approach inspired by techniques common in computer security and criminal investigation. We applied this new technique to a sample of 500 editing sessions and categorized each session based on what the editor seemed to be intending to do.
Most of the contributions we found fell into one of the two following categories:
Many contributions were quotidian attempts to add to the encyclopedia. Tor-based editors added facts, fixed typos, and updated train schedules. There s no way to know whether these individuals knew they were just getting lucky in their ability to edit or were patiently reloading to evade the ban.
Second, we found harassing comments and vandalism. Unwelcome conduct is common in online environments, and it is sometimes more common when the likelihood of being identified decreases. Some of the harassing comments we observed were direct responses to being banned as a Tor user.
Although these were most of what we observed, we also found evidence of several types of contributor intent:
We observed activism: when a contributor tried to draw attention to journalistic accounts of environmental and human rights abuses committed by a mining company, only to have editors linked to the mining company repeatedly remove their edits. Another example involved an editor trying to diminish the influence of proponents of alternative medicine.
We also observed quality maintenance activities when editors used Wikipedia s rules on appropriate sourcing to remove personal websites being cited in conspiracy theories.
We saw edit wars with Tor editors participating in a back-and-forth removal and replacement of content as part of a dispute, in some cases countering the work of an experienced Wikipedia editor whom even other experienced editors had gauged to be biased.
Finally, we saw Tor-based editors participating in non-article discussions, such as investigations into administrator misconduct and protests against the Wikipedia platform s mistrust of Tor editors.
An exploratory mapping of our themes in terms of the value a type of contribution represents to the Wikipedia community and the importance of anonymity in facilitating it. Anonymity-protecting tools play a critical role in facilitating contributions on the right side of the figure, while edits on the left are more likely to occur even when anonymity is impossible. Contributions toward the top reflect valuable forms of participation in Wikipedia, while edits at the bottom reflect damage.
In all, these themes led us to reflect on how the risks individuals face when contributing to online communities sometimes diverge from the risks the communities face in accepting their work. Expressing minoritized perspectives, maintaining community standards even when you may be targeted by the rulebreaker, highlighting injustice, or acting as a whistleblower can be very risky for an individual, and may not be possible without privacy protections. Of course, in platforms seeking to support the public good, such knowledge and accountability may be crucial.
This work was published as a paper at CSCW: Kaylea Champion, Nora McDonald, Stephanie Bankes, Joseph Zhang, Rachel Greenstadt, Andrea Forte, and Benjamin Mako Hill. 2019. A Forensic Qualitative Analysis of Contributions to Wikipedia from Anonymity Seeking Users. Proceedings of the ACM on Human-Computer Interaction. 3, CSCW, Article 53 (November 2019), 26 pages. https://doi.org/10.1145/3359155
This project was conducted by Kaylea Champion, Nora McDonald, Stephanie Bankes, Joseph Zhang, Rachel Greenstadt, Andrea Forte, and Benjamin Mako Hill. This work was supported by the National Science Foundation (awards CNS-1703736 and CNS-1703049) and included the work of two undergraduates supported through an NSF REU supplement.
Part
3: Building the Keystone Dataproc Custom Images for Secure Boot &
GPUs
In Part 1, we established a secure, proxy-only network. In Part 2, we
explored the enhanced install_gpu_driver.sh initialization
action. Now, in Part 3, we ll focus on using the LLC-Technologies-Collier/custom-images
repository (branch proxy-exercise-2025-11) to build the
actual custom Dataproc images embedded with NVIDIA drivers signed for
Secure Boot, all within our proxied environment.
Why Custom Images?
To run NVIDIA GPUs on Shielded VMs with Secure Boot enabled, the
NVIDIA kernel modules must be signed with a key trusted by the VM s EFI
firmware. Since standard Dataproc images don t include these
custom-signed modules, we need to build our own. This process also
allows us to pre-install a full stack of GPU-accelerated software.
The custom-images Toolkit
(examples/secure-boot)
The examples/secure-boot directory within the custom-images repository contains the necessary scripts and
configurations, refined through significant development to handle proxy
and Secure Boot challenges.
Key Components & Development Insights:
env.json: The central configuration
file (as used in Part 1) for project, network, proxy, and bucket
details. This became the single source of truth to avoid configuration
drift.
create-key-pair.sh: Manages the Secure
Boot signing keys (PK, KEK, DB) in Google Secret Manager, essential for
the module signing.
build-and-run-podman.sh: Orchestrates
the image build process in an isolated Podman container. This was
introduced to standardize the build environment and encapsulate
dependencies, simplifying what the user needs to install locally.
pre-init.sh: Sets up the build
environment within the container and calls generate_custom_image.py. It crucially passes metadata
derived from env.json (like proxy settings and Secure Boot
key secret names) to the temporary build VM.
generate_custom_image.py: The core
Python script that automates GCE VM creation, runs the customization
script, and creates the final GCE image.
gce-proxy-setup.sh: This script from startup_script/ is vital. It s injected into the temporary build VM and runs first to configure the OS, package
managers (apt, dnf), tools (curl, wget, GPG), Conda, and Java to use the
proxy settings passed in the metadata. This ensures the entire build
process is proxy-aware.
install_gpu_driver.sh: Used as the --customization-script within the build VM. As detailed in
Part 2, this script handles the driver/CUDA/ML stack installation and
signing, now able to function correctly due to the proxy setup by gce-proxy-setup.sh.
Layered Image Strategy:
The pre-init.sh script employs a layered approach:
secure-boot Image: Base image with
Secure Boot certificates injected.
tf Image: Based on secure-boot, this image runs the full install_gpu_driver.sh within the proxy-configured build VM
to install NVIDIA drivers, CUDA, ML libraries (TensorFlow, PyTorch,
RAPIDS), and sign the modules. This is the primary target image for our
use case.
(Note: secure-proxy and proxy-tf layers
were experiments, but the -tf image combined with runtime
metadata emerged as the most effective solution for 2.2-debian12).
Build Steps:
Clone Repos & Configure env.json: Ensure you have the custom-images and cloud-dataproc repos and a
complete env.json as described in Part 1.
Run the Build: bash # Example: Build a 2.2-debian12 based image set # Run from the custom-images repository root bash examples/secure-boot/build-and-run-podman.sh 2.2-debian12
This command will build the layered images, leveraging the proxy
settings from env.json via the metadata injected into the
build VM. Note the final image name produced (e.g., dataproc-2-2-deb12-YYYYMMDD-HHMMSS-tf).
Conclusion of Part 3
Through an iterative process, we ve developed a robust workflow
within the custom-images repository to build Secure
Boot-compatible GPU images in a proxy-only environment. The key was
isolating the build in Podman, ensuring the build VM is fully
proxy-aware using gce-proxy-setup.sh, and leveraging the
enhanced install_gpu_driver.sh from Part 2.
In Part 4, we ll bring it all together, deploying a Dataproc cluster
using this custom -tf image within the secure network, and
verifying the end-to-end functionality.
Part
2: Taming the Beast Deep Dive into the Proxy-Aware GPU Initialization
Action
In Part 1 of this series, we laid the network foundation for running
secure Dataproc clusters. Now, let s zoom in on the core component
responsible for installing and configuring NVIDIA GPU drivers and the
associated ML stack in this restricted environment: the install_gpu_driver.sh script from the LLC-Technologies-Collier/initialization-actions
repository (branch gpu-202601).
This isn t just any installation script; it has been significantly
enhanced to handle the nuances of Secure Boot and to operate seamlessly
behind an HTTP/S proxy.
The
Challenge: Installing GPU Drivers Without Direct Internet
Our goal was to create a Dataproc custom image with NVIDIA GPU
drivers, sign the kernel modules for Secure Boot, and ensure the entire
process works seamlessly when the build VM and the eventual cluster
nodes have no direct internet access, relying solely on an HTTP/S proxy.
This involved:
Proxy-Aware Build: Ensuring all build steps within
the custom image creation process (package downloads, driver downloads,
GPG keys, etc.) correctly use the customer s proxy.
Secure Boot Signing: Integrating kernel module
signing using keys managed in GCP Secret Manager, especially when
drivers are built from source.
Conda Environment: Reliably and speedily installing
a complex Conda environment with PyTorch, TensorFlow, Rapids, and other
GPU-accelerated libraries through the proxy.
Dataproc Integration: Making sure the custom image
works correctly with Dataproc s own startup, agent processes, and
cluster-specific configurations like YARN.
The
Development Journey: Key Enhancements in install_gpu_driver.sh
To address these challenges, the script incorporates several key
features:
Robust Proxy Handling (set_proxy
function):
Challenge: Initial script versions had spotty proxy
support. Many tools like apt, curl, gpg, and even gsutil failed in proxy-only
environments.
Enhancements: The set_proxy function
(also used in gce-proxy-setup.sh) was completely overhauled
to parse various proxy metadata (http-proxy, https-proxy, proxy-uri, no-proxy). Critically, environment variables
(HTTP_PROXY, HTTPS_PROXY, NO_PROXY) are now set before any network
operations. NO_PROXY is carefully set to include .google.com and .googleapis.com to allow
direct access to Google APIs via Private Google Access. System-wide
trust stores (OS, Java, Conda) are updated with the proxy s CA
certificate if provided via http-proxy-pem-uri. gcloud, apt, dnf, and dirmngr are also configured to use the proxy.
Challenge: Importing GPG keys for repositories
often failed as keyservers use non-HTTP ports (e.g., 11371) blocked by
firewalls, and gpg --recv-keys is not proxy-friendly.
Solution: A new import_gpg_keys
function now fetches keys over HTTPS using curl, which
respects the environment s proxy settings. This replaced all direct gpg --recv-keys calls.
GCS Caching is King:
Challenge: Repeatedly downloading large files
(drivers, CUDA, source code) through a proxy is slow and
inefficient.
Solution: Implemented extensive GCS caching for
NVIDIA drivers, CUDA runfiles, NVIDIA Open Kernel Module source
tarballs, compiled kernel modules, and even packed Conda environments.
Scripts now check a GCS bucket (dataproc-temp-bucket)
before hitting the internet.
Impact: Dramatically speeds up subsequent runs and
init action execution times on cluster nodes after the cache is
warmed.
Conda Environment Stability & Speed:
Challenge: Large Conda environments are prone to
solver conflicts and slow installation times.
Solution: Integrated Mamba for faster package
solving. Refined package lists for better compatibility. Added logic to
force-clean and rebuild the Conda environment cache on GCS and locally
if inconsistencies are detected (e.g., driver installed but Conda env
not fully set up).
Secure Boot & Kernel Module Signing:
Challenge: Custom-compiled kernel modules must be
signed to load when Secure Boot is enabled.
Solution: The script integrates with GCP Secret
Manager to fetch signing keys. The build_driver_from_github
function now includes robust steps to compile, sign (using sign-file), install, and verify the signed modules.
Custom Image Workflow & Deferred Configuration:
Challenge: Cluster-specific settings (like YARN GPU
configuration) should not be baked into the image.
Solution: The install_gpu_driver.sh
script detects when it s run during image creation
(--metadata invocation-type=custom-images). In this mode,
it defers cluster-specific setups to a systemd service
(dataproc-gpu-config.service) that runs on the first boot
of a cluster instance. This ensures that YARN and Spark configurations
are applied in the context of the running cluster, not at image build
time.
Conclusion of Part 2
The install_gpu_driver.sh initialization action is more
than just an installer; it s a carefully crafted tool designed to handle
the complexities of secure, proxied environments. Its robust proxy
support, comprehensive GCS caching, refined Conda management, Secure
Boot signing capabilities, and awareness of the custom image build
lifecycle make it a critical enabler.
In Part 3, we ll explore how the LLC-Technologies-Collier/custom-images
repository (branch proxy-exercise-2025-11) uses this
initialization action to build the complete, ready-to-deploy Secure Boot
GPU custom images.
Part
1: Building a Secure Network Foundation for Dataproc with GPUs &
SWP
Welcome to the first post in our series on running GPU-accelerated
Dataproc workloads in secure, enterprise-grade environments. Many
organizations need to operate within VPCs that have no direct internet
egress, instead routing all traffic through a Secure Web Proxy (SWP).
Additionally, security mandates often require the use of Shielded VMs
with Secure Boot enabled. This series will show you how to meet these
requirements for your Dataproc GPU clusters.
In this post, we ll focus on laying the network foundation using
tools from the LLC-Technologies-Collier/cloud-dataproc
repository (branch proxy-sync-2026-01).
The Challenge: Network
Isolation & Control
Before we can even think about custom images or GPU drivers, we need
a network environment that:
Prevents direct internet access from Dataproc cluster nodes.
Forces all egress traffic through a manageable and auditable
SWP.
Provides the necessary connectivity for Dataproc to function and for
us to build images later.
Supports Secure Boot for all VMs.
The Toolkit: LLC-Technologies-Collier/cloud-dataproc
To make setting up and tearing down these complex network
environments repeatable and consistent, we ve developed a set of bash
scripts within the gcloud directory of the cloud-dataproc repository. These scripts handle the
creation of VPCs, subnets, firewall rules, service accounts, and the
Secure Web Proxy itself.
Key Script: gcloud/bin/create-dpgce-private
This script is the cornerstone for creating the private, proxied
environment. It automates:
VPC and Subnet creation (for the cluster, SWP, and management).
Setup of Certificate Authority Service and Certificate Manager for
SWP TLS interception.
Deployment of the SWP Gateway instance.
Configuration of a Gateway Security Policy to control egress.
Creation of necessary firewall rules.
Result: Cluster nodes in this VPC have NO default
internet route and MUST use the SWP.
Configuration via env.json
We use a single env.json file to drive the
configuration. This file will also be used by the custom-images scripts in Part 3. This env.json
should reside in your custom-images repository clone, and
you ll symlink it into the cloud-dataproc/gcloud
directory.
Running the Setup:
# Assuming you have cloud-dataproc and custom-images cloned side-by-side# And your env.json is in the custom-images rootcd cloud-dataproc/gcloud# Symlink to the env.json in custom-imagesln-sf ../../custom-images/env.json env.json# Run the creation script, but don't create a cluster yetbash bin/create-dpgce-private --no-create-clustercd ../../custom-images
Node
Configuration: The Metadata Startup Script for Runtime
For the Dataproc cluster nodes to function correctly in this proxied
environment, they need to be configured to use the SWP on boot. We
achieve this using a GCE metadata startup script.
The script startup_script/gce-proxy-setup.sh (from the custom-images repository) is designed to be run on each
cluster node at boot. It reads metadata like http-proxy and http-proxy-pem-uri (which our cluster creation scripts in
Part 4 will pass) to configure the OS environment, package managers, and
other tools to use the SWP.
Upload this script to your GCS bucket:
# Run from the custom-images repository rootgsutil cp startup_script/gce-proxy-setup.sh gs://$(jq-r .BUCKET env.json)/custom-image-deps/
This script is essential for the runtime behavior of the
cluster nodes.
Conclusion of Part 1
With the cloud-dataproc scripts, we ve laid the
groundwork by provisioning a secure VPC with controlled egress through
an SWP. We ve also prepared the essential node-level proxy configuration
script (gce-proxy-setup.sh) in GCS, ready to be used by our
clusters.
Stay tuned for Part 2, where we ll dive into the install_gpu_driver.sh initialization action from the LLC-Technologies-Collier/initialization-actions repository
(branch gpu-202601) and how it s been adapted to install
all GPU-related software through the proxy during the image build
process.
This post covers my friend Badri and my experiences in Brunei. Brunei officially Brunei Darussalam is a country in Southeast Asia, located on Borneo island. It is one of the few remaining absolute monarchies on Earth.
On the morning of the 10th of December 2024, Badri and I reached Brunei International Airport by taking a flight from Kuala Lumpur. Upon arrival at the airport, we had to go through the immigration, of course. However, I forgot to fill my arrival card, which I filled while I was in the queue for my immigration.
The immigration officer asked me how much cash I was carrying of each currency. After completing the formalities, the immigration officer stamped my passport and let me in. Take a look at Brunei s entry stamp in my passport.
Brunei entry stamp on my passport. Picture by Ravi Dwivedi, released under CC-BY-SA 4.0.
We exchanged Singapore dollars to get some Brunei dollars at the airport. The Brunei dollar was pegged 1:1 with the Singapore dollar, meaning 1 Singapore dollar equals 1 Brunei dollar. The exchange rate we received at the airport was the same.
Our (pre-booked) accommodation was located near Gadong mall. So, we went to the information center at the airport to ask how to get there by public transport. However, the person at the information center told us that they didn t know the public transport routes and suggested we take a taxi instead.
We came out of the airport and came across an Indian with a mini bus. He offered to drop us at our accommodation for 10 Brunei dollars ( 630). As we were tired after a sleepless night, we didn t negotiate and took the offer. It felt a bit weird using the minibus as our private taxi.
In around half-an-hour, we reach our accommodation. The place was more like a guest house than a hotel. In addition to the rooms, it had common space consisting of a hall, a kitchen and a balcony.
Our room in Brunei. Picture by Ravi Dwivedi, released under CC-BY-SA 4.0
Upon reaching the place, we paid for our room in cash, which was 66.70 Singapore dollars (4200 Indian rupees) for two nights. We reached before the check-in time, so we had to wait for our room to get ready before we entered.
The room had a double bed and also a place to hang clothes. We slept for a few hours before going out at night. We went into Gadong mall and had coffee at a caf named The Coffee Bean & Tea Leaf. The regular caffe latte I had here been 5.20 Brunei dollars. On another note, the snacks we got us from Kuala Lumpur covered us for the dinner.
The next day 11th of December 2024 we went to a nearby restaurant named Nadj for lunch. The owner was from Kerala. Here we ordered:
1 paneer pepper masala for 5 Brunei dollars (320 rupees)
1 Nasi goreng pattaya biasa for 4.50 Brunei dollars (290 rupees)
1 plain naan for 1.50 Brunei dollars (100 rupees)
1 butter naan for 1.80 Brunei dollars (115 rupees)
So, our lunch cost a total of 12.80 Brunei dollars (825 rupees). The naan was unusually thick, and didn t like the taste.
After the lunch, we planned to visit Brunei s famous Omar Ali Saifuddien Mosque. However, a minibus driver outside of Gadong Mall told us that the mosque would be closed in half-an-hour and suggested we visit the nearby Jame Asr Hassanil Bolkiah Mosque instead.
Jame Asr Hassanil Bolkiah Mosque. Picture by Ravi Dwivedi, released under CC-BY-SA 4.0
He dropped us there for 1 Brunei dollar per person. The person hailed from Uttar Pradesh and told us about bus routes in Hindi. Buses routes in Brunei were confusing, so the information he gave us was valuable.
It was evening, and we had an impression that the mosque and its premises were closed. However, soon enough, we stumbled across an open gate entering the mosque complex. We walked inside for some time, took pictures and exited. Walking in Bandar Seri Begawan wasn t pleasant, though. The pedestrian infrastructure wasn t good.
Then we walked back to our place and bought some souvenirs. For dinner and breakfast, we bought bread, fruits and eggs from local shops as we had a kitchen to cook for ourselves.
The guest house also had a washing machine (free of charge) which we wanted to use. However, they didn t have detergent. Therefore, we went outside to get some detergent. It was 8 o clock, and most of the shops were closed already. Others had had detergents in large sizes, the ones you would use if you lived there. We ended up getting a small packet at a supermarket.
The next day 12th of December we had a flight to Ho Chi Minh City in Vietnam with a long layover in Kuala Lumpur. We had breakfast in the morning and took a bus to Omar Ali Saifuddien Mosque. The mosque was in prayer session, so it was closed for Muslims. Therefore, we just took pictures from the outside and took a bus for the airport.
Omar Ali Saifuddien Mosque. Picture by Ravi Dwivedi, released under CC-BY-SA 4.0
When the bus reached near the airport, the bus went straight rather than taking a left turn for the airport. Initially, I thought the bus would just take a turn and come back. However, the bus kept going away from the airport. Confused by this, I asked other passengers if the bus was going to the airport. The driver stopped the bus at Muara Town terminal 20 km from the airport. At this point, everyone alighted, except for us. The driver went to a nearby restaurant to have lunch.
I felt very uncomfortable stranded in a town which was 20 km from the airport. We had a lot of time, but I was still worried about missing our flight, as I didn t want to get stuck in Brunei. After waiting for 15 minutes, I went inside the restaurant and reminded the driver that we had a flight in a couple of hours and needed to go to the airport. He said he will leave soon.
When he was done with his lunch, he drove us to the airport. It was incredibly frustrating. On a positive note, we saw countryside of Brunei that we would have seen otherwise. The bus ride cost us 1 Brunei dollar each.
A shot of Brunei s countryside. Picture by Ravi Dwivedi, released under CC-BY-SA 4.0.
That s it for this one. Meet you in the next one. Stay tuned for the Vietnam post!
A new release, now at version 0.0.22, of RcppAnnoy
has arrived on CRAN, just a
little short of two years since the previous release.
RcppAnnoy
is the Rcpp-based R integration of
the nifty Annoy library
by Erik Bernhardsson. Annoy is a small and
lightweight C++ template header library for very fast approximate
nearest neighbours originally developed to drive the Spotify music discovery algorithm. It
had all the buzzwords already a decade ago: it is one of the algorithms
behind (drum roll ) vector search as it finds
approximate matches very quickly and also allows to
persist the data.
This release contains three contributed pull requests covering a new
metric, a new demo and quieter compilation, some changes to
documentation and last but not least general polish including letting
the vignette now use the Rcpp::asis builder.
Details of the release follow based on the NEWS file.
Changes in version 0.0.23
(2026-01-12)
Add dot product distance metrics (Benjamin James in #78)
Apply small polish to the documentation (Dirk closing #79)
A new demo() has been added (Samuel Granjeaud in #79)
Switch to Authors@R in DESCRIPTION
Several updates to continuous integration and README.md
Small enhancements to package help files
Updates to vignettes and references
Vignette now uses Rcpp::asis builder (Dirk in #80)
Switch one macro to a function to avoid a compiler nag (Amos
Elberg in #81)
Note: I have not published blog posts about my academic papers over the past few years. To ensure that my blog contains a more comprehensive record of my published papers and to surface these for folks who missed them, I will be periodically (re)publishing blog posts about some older published projects. This particular post is closely based on a previously published post by Nate TeBlunthuis from the Community Data Science Blog.
Many online platforms are adopting AI and machine learning as a tool to maintain order and high-quality information in the face of massive influxes of user-generated content. Of course, AI algorithms can be inaccurate, biased, or unfair. How do signals from AI predictions shape the fairness of online content moderation? How can we measure an algorithmic flagging system s effects?
In our paper published at CSCW, Nate TeBlunthuis, together with myself and Aaron Halfaker, analyzed the RCFilters system: an add-on to Wikipedia that highlights and filters edits that a machine learning algorithm called ORES identifies as likely to be damaging to Wikipedia. This system has been deployed on large Wikipedia language editions and is similar to other algorithmic flagging systems that are becoming increasingly widespread. Our work measures the causal effect of being flagged in the RCFilters user interface.
Screenshot of Wikipedia edit metadata on Special:RecentChanges with RCFilters enabled. Highlighted edits with a colored circle to the left side of other metadata are flagged by ORES. Different circle and highlight colors (white, yellow, orange, and red in the figure) correspond to different levels of confidence that the edit is damaging. RCFilters does not specifically flag edits by new accounts or unregistered editors, but does support filtering changes by editor types.
Our work takes advantage of the fact that RCFilters, like many algorithmic flagging systems, create discontinuities in the relationship between the probability that a moderator should take action and whether a moderator actually does. This happens because the output of machine learning systems like ORES is typically a continuous score (in RCFilters, an estimated probability that a Wikipedia edit is damaging), while the flags (in RCFilters, the yellow, orange, or red highlights) are either on or off and are triggered when the score crosses some arbitrary threshold. As a result, edits slightly above the threshold are both more visible to moderators and appear more likely to be damaging than edits slightly below. Even though edits on either side of the threshold have virtually the same likelihood of truly being damaging, the flagged edits are substantially more likely to be reverted. This fact lets us use a method called regression discontinuity to make causal estimates of the effect of being flagged in RCFilters.
Charts showing the probability that an edit will be reverted as a function of ORES scores in the neighborhood of the discontinuous threshold that triggers the RCfilters flag. The jump in the increase in reversion chances is larger for registered editors compared to unregistered editors at both thresholds.
To understand how this system may affect the fairness of Wikipedia moderation, we estimate the effects of flagging on edits on different groups of editors. Comparing the magnitude of these estimates lets us measure how flagging is associated with several different definitions of fairness. Surprisingly, we found evidence that these flags improved fairness for categories of editors that have been widely perceived as troublesome particularly unregistered (anonymous) editors. This occurred because flagging has a much stronger effect on edits by the registered than on edits by the unregistered.
We believe that our results are driven by the fact that algorithmic flags are especially helpful for finding damage that can t be easily detected otherwise. Wikipedia moderators can see the editor s registration status in the recent changes, watchlists, and edit history. Because unregistered editors are often troublesome, Wikipedia moderators attention is often focused on their contributions, with or without algorithmic flags. Algorithmic flags make damage by registered editors (in addition to unregistered editors) much more detectable to moderators and so help moderators focus on damage overall, not just damage by suspicious editors. As a result, the algorithmic flagging system decreases the bias that moderators have against unregistered editors.
This finding is particularly surprising because the ORES algorithm we analyzed was itself demonstrably biased againstunregistered editors (i.e., the algorithm tended to greatly overestimate the probability that edits by these editors were damaging). Despite the fact that the algorithms were biased, their introduction could still lead to less biased outcomes overall.
Our work shows that although it is important to design predictive algorithms to avoid such biases, it is equally important to study fairness at the level of the broader sociotechnical system. Since we first published a preprint of our paper, a follow-up piece by Leijie Wang and Haiyi Zhu replicated much of our work and showed that differences between different Wikipedia communities may be another important factor driving the effect of the system. Overall, this work suggests that social signals and social context can interact with algorithmic signals, and together these can influence behavior in important and unexpected ways.
The full citation for the paper is: TeBlunthuis, Nathan, Benjamin Mako Hill, and Aaron Halfaker. 2021. Effects of Algorithmic Flagging on Fairness: Quasi-Experimental Evidence from Wikipedia. Proceedings of the ACM on Human-Computer Interaction 5 (CSCW): 56:1-56:27. https://doi.org/10.1145/3449130.
We have also released replication materials for the paper, including all the data and code used to conduct the analysis and compile the paper itself.