Tarn

One day on a real network.
Find the attacker.

Each point is a person or a computer from a national lab's login records. Red lines are a real attack. As the day plays, the alerts an AI analyst was handed arrive with its answers.

- logins over - days. - rows in the answer key.

-

Would the detector have caught them?

Version 2 draws a map of who logged into which computer and scores each login as it arrives, using only what happened before it. Each day the highest scores become alerts. Every detector gets the same number of alerts a day, since that is what one person can read.

Compromised account-days each detector put in front of that person, out of every one in the test days.

Ask the data yourself live in your browser

DuckDB, a database compiled to run inside a web page, answers your SQL on your own machine, against the real results from the pipeline. It is a 35 MB download, so it starts when you reach this part of the page, not before.

Waiting. It starts when you scroll here.
What your browser downloaded
Nothing yet.

Describe a day as 52 numbers, then look for days that rhyme live in your browser

Every person-day becomes a list of 52 numbers: how much they did, how often they failed, what hours they were active, what kinds of login they used. Similar behaviour gives similar numbers, so the computer can fetch the nearest matches. Nothing from the answer key goes into those numbers, or the search would just be finding the answers.

Waiting for the database.

Simple rules against vector search, same budget

Each gets the same number of people to accuse. Who finds more of the attack?

As a way of spotting the attack on its own, this lost. Told to find unusual behaviour with no other help, it does worse than the simple rules at every budget but one, and the one it wins is the rule that caught nothing at all. Being unusual and being an attacker are not the same thing.

As a way of finding more of an attack you already found, it is very good. Hand it one confirmed bad day and - of what comes back is also the attack, which is - better than picking days at random. It cannot find the first one for you. It can find the rest.

Under the hood: the version 1 platform

Before the graph detector there was a data platform: Spark to clean and summarise the raw logs, a DuckDB warehouse to query them, a streaming job to count logins as they arrive, and a Neo4j graph of who can reach what. Everything below was measured on one laptop and is replayed from those recordings.

Getting it to run faster, without cheating

The job that summarises the logins used to read every event twice: once to count what each person did, and again to find machines they had never used before. Now it builds one shared summary and both steps read that. The easy way to fake a speed-up is to quietly do less work, so all four versions were run and their answers compared. They match exactly.

How long each version took

Shorter is better. Five runs each, the middle one shown.

Proof it is not just doing less work

Same row count and the same fingerprint of the data, every version.

Spark would normally pick the second row's trick by itself, so that row shows what the slower approach costs rather than a bug that got fixed.

The slow version, as the engine planned it
-
The fast version, as the engine planned it
-

-

Counting logins as they arrive

The same data pushed through a message queue instead of read from a file. A streaming job counts every minute of activity per person. Late events still land in the right minute, and a killed job restarts where it stopped without counting twice. Most of the delay is the job waiting for stragglers before it closes a minute. If it were near zero, late events would be getting thrown away.

Time from arriving to being counted

Half finish faster than the first bar. Nearly all beat the last one.

What the run did

Settings and throughput, as configured

A replay from a file into a queue on the same laptop as the reader. No network, no cluster. It measures this pipeline, not a production system.

-

Two people who use the same machine are connected

If someone steals my account, and I log into a server a colleague also uses, they now have a route towards that colleague. Chain those steps and you have a path through the company. Pick two people and the shortest route lights up. These are real answers from the graph database, worked out in advance, because a browser cannot run that kind of query.

  • a person
  • a machine
  • machine the attack came from
  • machine the attack reached
  • route between your two picks

Loading the graph when you get here.

Pick two people to trace a route.
How far can a stolen account reach?

Two very different scales, so two separate charts

machines they log into themselves

machines they can get to in three steps, out of every machine there is

Would any of the version 1 rules have caught them?

Each warning sign, scored against the answer key

The machines everybody uses

The finding that held up. A few machines are used by tens of thousands of people each, which puts almost everyone two steps from almost everyone else. Lock these down and most routes break. That is something you can act on.

Odd hours had to be measured, not guessed

The log has no clock times, only seconds since recording began, so "logged in at 3am" cannot be read off it. The first attempt found a flat line, and that was correct: most logins are machines checking in with each other around the clock. Separate out the people and their rhythm is plain, a dip overnight and a peak mid-morning.

Then it caught nothing. As a way of spotting the attacker this careful piece of work scored zero out of 181. It sits at the bottom of the rules table above.

Logins by hour of the day
-