← back to blog

Why wallets get clustered by timing

sybil clustering wallet-ops timing opsec

Wallet nine was always last.

I found that while building a spend sheet, which meant sorting transactions by time for the first time. Four months of activity, and in every session the same wallet went last, with the rest lined up in front of it in the same sequence.

There was no thinking behind it. That was the order of the tabs in my browser, and before that it had been the order of the rows in a spreadsheet.

Those wallets were separated on purpose. Separate funding paths, separate profiles, a couple of them on separate networks. The thing they had in common was that one person worked through them from the top down, every time, for four months.

The dimension nobody separates

People put real money into separation. Addresses cost money. Isolated profiles cost time. The funding graph gets thought about properly, and it deserves the attention.

Then all of it happens inside one evening.

The clock never gets treated as data because it does not feel like something you are handing over. Every other signal is something you produce. Time is what happens to you while you produce the others.

It also cannot be switched off. A timestamp is not an extra field somebody decided to collect, since ordering is the entire job of a chain. The record of when has to exist for the thing to function.

What the query costs

The part that persuaded me came from the infrastructure side of my week rather than the farming side.

I sell mobile proxy lines. A SIM runs about ten dollars a month, a modem loses roughly $1.50 a month across the life I get out of it, and a powered hub for thirty ports is $150. Every capability has a price, and the price decides whether anyone bothers.

Clustering by device fingerprint means collecting fingerprints, storing them and keeping them current. Clustering by off chain identity means an exchange relationship or a data supplier, and sometimes a lawyer. The funding graph needs indexed chain data and somebody who knows how to walk it.

Clustering by time is a group by. Take addresses you already hold, bucket the activity by hour, see what moves together. An afternoon of work and nothing to buy.

That gap is my position on this whole subject. Timing is the cheapest signal to compute and the most expensive one to defeat, and the ratio is never going to improve.

The silence is the same signal

Correlated activity is the obvious half. Six wallets acting inside ten minutes look like six wallets acting inside ten minutes.

Correlated inactivity does the same work and almost nobody thinks about it. Go away for nine days and every wallet you own goes quiet on the same afternoon and wakes up on the same evening.

People schedule their activity. I have never met anybody who schedules their gaps.

The gaps are also where the identifying detail sits. You travelled. You had flu. A fortnight disappeared into work. Those holes belong to one specific life, and a set of wallets sharing them is a set of wallets sharing a person.

Waking hours

Plot hour of day for a single wallet across a year and you get a lumpy shape with a fat part in it. The fat part is somebody’s evening.

Do it for thirty wallets belonging to thirty strangers and those fat parts land all over the dial, because those people have different jobs and different time zones and different children.

Do it for thirty wallets belonging to one person and the same evening shows up thirty times.

Mine was seventy one percent between ten at night and one in the morning. No plan produced that. It is simply the hour the house goes quiet.

Worth saying that this is the one place where the browser layer gives you nothing. A profile can report any timezone it likes, and the block still records the minute your transaction actually arrived.

The gap before the first move

Money lands in a wallet, and at some point that wallet does its first thing. The distance between those two events is a number, and nobody thinks of it as one.

For a real person that number is chaotic. Funded and used inside a minute. Funded, forgotten, touched five weeks later because something reminded you.

For wallets set up in one sitting it is oddly consistent, because what it really measures is how fast the operator got through their own checklist.

Where the money came from is a separate topic with its own answer. The latency is a different thing, and it survives even when the funding came from four unrelated places.

Everybody hears the news at the same moment

The last one is outside your control, which is why careful people still end up grouped.

A program gets announced. A claim opens. A deadline appears. Your wallets all move, and they move together, because one person read one thread once and then went and did the thing on everything they own.

The announcement has a public timestamp, so the analyst gets a free reference clock. Your wallets do not have to be synchronised with each other for this to work. They only have to be synchronised with the news.

That one is unpleasant, because reacting quickly is exactly what the incentive structure is asking for.

Why jitter does not fix it

The standard answer is a random delay in front of every action.

A random delay is a distribution, and a distribution is a fingerprint. Uniform random across six hours produces an even smear over the clock, and no human being has ever produced an even smear over the clock.

Real timing is bursty. Five things in twenty minutes, then nine days of nothing, then one transaction at four in the morning because you woke up and remembered. Gaps between real actions crowd at the short end with a long thin tail hanging off them. Jitter has neither property, so the timing is now strange in a fresh way rather than an obvious one.

Scale makes it worse. Thirty wallets with a random offset around one habit make the habit easier to find, because thirty samples pin a mean far better than three do. The randomness blurs the shape and the blur thins out as you add wallets.

Why a scheduler does not either

The next attempt is to hand timing to a machine and spread the wallets across the whole day.

Machines have a signature of their own. Intervals that are too even. Actions landing on the minute. Sessions that never once fail to happen. Real people miss things and a cron job does not.

So you make the schedule irregular, and now you are inventing a distribution and hoping it passes for a life.

I watch this play out at the login layer on my own network. The setups that hold up are the small ones. Four things done properly at hours somebody was genuinely at a desk beat forty on a timer, every time I have seen it go.

The record does not improve

Here is the property specific to time.

Every other signal can be fixed going forward. Cleaner addresses next month, a rebuilt device layer next week, and the improvement applies from the moment you make it.

Timestamps are already written. Last season’s rhythm sits in the chain at the resolution it was recorded at, and no amount of care today edits it. Three months of batching everything into Sunday nights is finished and permanent, waiting for whoever looks at it next year with better tools than exist now.

What the real fix costs

Fewer wallets, doing genuinely different things at genuinely different times, for reasons that actually turned up on different days.

That is expensive in the way that counts. It caps how many you can run, which caps the upside, and running more of them was the point of automating in the first place.

A wallet that acted because you had a reason on a Tuesday afternoon shares no rhythm with anything. A wallet that acted because it was sixth in the batch drags the whole batch along behind it.

I cannot make that cheaper and neither can anyone selling a tool. Almost nobody does it because of the arithmetic, and in the short run the arithmetic points the other way.

What I cannot tell you

I have never seen the inside of a filter. I do not know which column got a wallet of mine zeroed and I never will, because the teams that describe their methods describe them afterwards and in general terms.

Everything above is reasoning from outside about what is cheap to compute and hard to fake. It has not been tested against a control group and I have no way to run one.

What I can tell you is that the timestamp is in every block, that reading it takes one query, and that none of the countermeasures I have watched people pay for go anywhere near it.

The rest of how I run this side of things is on the home page.

Get new guides and videos first — join the Telegram channel.

need infra for this today?