Five Riders of the Storm

Part three of the transit trilogy, after the portal and the phone: the open data. Agencies publish ridership as open data and call it anonymized. It is not. A trip is hidden only among the riders who made the same trip in the same hour, and when that count is small, one anchor turns it into a name. Grounded in the mobility-unicity research and a runnable auditor that measures where a release re-identifies people and proves the fix. Defensive by design. No one was re-identified.

This is the third time I have written about transit, and the pattern has held each time. The first was OMNY's account portal, where the fare rails were fine and the soft account layer around them was not. The second was the phone, where the Secure Element holds and the trust built around it is what cracks. This one moves to the quietest layer of all, the data the agency hands out on purpose, and the pattern holds a third time. The protection is real. The thing next to it is the exposure.

The MTA publishes a dataset called the Subway Origin-Destination Ridership Estimate, available right now on data.ny.gov, one file per year from 2021 forward. It is a genuinely good civic release. For every populated origin-destination pair it gives an estimated rider count, broken out by month, day of week, and hour of day, with the station complexes and their approximate coordinates. Planners use it. Researchers use it. It carries no names and no card numbers, which is exactly why everyone involved can call it anonymized and mean it.

The counts are the problem.

Small cells, real names

A rider's trip is hidden only among the other people who made the same trip in the same time bucket. That hiding group is the rider count, and the count is published. When a given origin, destination, and hour has four hundred riders, the individual is genuinely lost in the crowd. When it has three, the crowd is three people. When it has one, there is no crowd at all. The file is telling you that in a typical such hour, exactly one person makes that trip, and it is telling everyone who downloads it.

This is not a hunch. De Montjoye and colleagues measured it in "Unique in the Crowd: The privacy bounds of human mobility" in 2013, and the number has aged well: four approximate time-and-place points single out about ninety-five percent of people in a mobility dataset. A small origin-destination-hour cell is one of those points, published in advance. An attacker breaks no encryption and touches no turnstile. They read the open file and wait for a single anchor, one social post with a location, one photo timestamp, one observed entry, to collapse a small group to a single person and attach a name to a routine.

The MTA's release is aggregated across days within a month, so the granularity is "a typical Tuesday at eight," not "this exact Tuesday," which softens the one-off trip and does nothing for the commuter whose whole value as a target is that the trip repeats. Routine is what re-identification feeds on, and a commute is routine by definition.

The auditor, and what it found

I did not want to describe this in the abstract, so I built the defender's version of it and ran it. It is public and it is called five_riders_of_the_storm. It reads a ridership-style release, measures every published cell against a k-anonymity threshold, and reports how much of the file re-identifies the rider. The data it runs on is synthetic and generated in place, shaped like the real schema. No actual rider, dataset, or agency is touched.

On a release of 389 trip cells, the audit flagged 141 of them, more than a third, sitting in a group of fewer than five riders. Sixty-one of those were a group of one. Scrubbing the names changed nothing, because the name was never the identifier. The count was.

as published:   cells= 389   k<5 cells= 141 (36.2%)   unique=  61   min_k=1

The fixes, and what they cost

The good news is that the fixes are old and well understood, and the audit measures each one instead of asserting it.

Small-cell suppression, dropping every cell below the threshold before publishing, closes the exposure completely. In the run it cost 0.7 percent of total ridership to remove every re-identifying cell, because the small cells are a large share of the trips but a tiny share of the people. That asymmetry is the entire argument. You protect dozens of specifically exposed individuals for almost nothing, and what you lose is the long tail of rare trips that some researcher genuinely wanted, which is a real tradeoff and worth naming out loud rather than hiding.

Temporal coarsening, widening the hourly buckets so counts rise, keeps every rider in the data and, on its own, does not finish the job. The same run still left almost eighteen percent of cells re-identifying after collapsing to four-hour windows. It is a partial measure, and the audit says so plainly rather than letting it look like a cure.

Differential privacy, a calibrated noise budget, is the rigorous complement to suppression and the direction a serious release should go. It carries its own utility tradeoff, it is easy to misconfigure, and it is not a sticker you apply at the end. The lab discusses it and does not implement it, because shipping a half-correct privacy mechanism labeled as a guarantee would be its own kind of lie. The named standards are here for a reason: k-anonymity for the floor, differential privacy for the real defense, and the NIST privacy guidance for the program around both.

Where the real danger is

The honest limit, stated before anyone finds it for me: this audit measures re-identification within one dataset. The world is not one dataset. A data broker's location feed, an app's harvested beacon log, a public check-in, any of them can collapse a cell this tool would call safe, and no single release prevents that. That is the unicity bound, and it is bigger than the MTA. The transit file is not the whole exposure. It is the free, clean, government-issued starting point that makes the rest of the exposure cheap.

Which is the quiet reason this one matters more than it looks. The portal and the phone need an attacker to do something. This one does not. The exposure is already shipped, in a civic spirit, to anyone who clicks download.

The part I did not build

I built the auditor and not the unmasker, and that was a choice, not a limit. The offensive version is short, and I know exactly where the anchor comes from, because that is the job. I did not write it down, because this release does not need me to. Anyone with the public file and one ordinary fact about a person is already most of the way there. The point was never that it can be done. The point is that it is done, continuously, by the design of the release.

So this is where the trilogy rests, for now. Run the audit before you publish. Suppress the small cells. Budget the noise. Treat a low count as the identifier it is.

And when a system needs the other version, the one that proves the whole chain end to end under scope, with permission and a report at the end, that is the work I do and I am ready when you are. I just prefer to show you the lock before I show you the key.