Why did it move? Rain, a dead sensor, or an attack

Ask why a reading moved and get one of four answers with the numbers behind it, or “I can’t tell”: the weather, the instrument, a change of operation, or a reading that no longer fits its neighbours. What it says, what it does not claim, and how it did on plants it had never seen.

Ask why a reading moved, and get an answer you can check.

A level jumps at three in the night. Was it the rain, a sensor that died, someone changing a setpoint, or something that should not be there? The person on shift has to decide, often alone.

Ask about a reading and a stretch of time, in the console or through your own AI assistant. You get one of four answers, the weather, the instrument, a change of operation, or a reading that no longer fits its neighbours, each with the numbers behind it. Or you get “I can’t tell”, with the reason. The answer is worked out from the plant’s own readings; no language model decides it.

It is built to avoid the costliest mistake first: blaming a dead sensor on the rain, so that nobody goes out to a pump that is fine while the real fault stays. And it never says “you are under attack”. When a reading looks healthy but no longer fits the readings around it, it says so, says it could be an attack or a sensor failing slowly, and asks you to check it on site.

It rained, but it was the sensor

Blue Heart sewer network, Eastbourne, United Kingdom

“Most likely the instrument or its data, not the process. ESH8D_Terminus Rd Water Level [mm] fell to -250.6 mm, far below what it read before (1st percentile 252.4 mm). It rained 18.2 mm, but rain does not do this to a reading. All of this was learned from data before 2025-07-21T01:45:00Z.”

The weather

Blue Heart sewer network, Eastbourne, United Kingdom

“Most likely the weather: EASTBOURNE WTW (TQ58007405) Sewer Level [mm] rose after 28.2 mm of rain in the 12 hours before, and its data looks healthy. All of this was learned from data before 2025-07-19T04:15:00Z.”

A change of operation

HAI security testbed, with hardware in the loop (a testbed, not a working plant)

“A change of operation: x1002_07_SETPOINT_OUT went from 354.7 to 390.9, and P1_LIT01 followed it the way it has before. Not a fault. All of this was learned from data before 2022-08-07T16:14:31Z.”

It does not fit its neighbours

HAI security testbed, with hardware in the loop (a testbed, not a working plant)

“P1_PIT01 looks healthy on its own, but it does not fit its neighbours (P4_ST_PT01, P1_LCV01D, P1_LCV01Z) any more: it missed what they predict by 6.77 times the usual error. It could be an attack, or a sensor failing slowly. Check it on site. All of this was learned from data before 2022-08-12T17:35:01Z.”

These are the explainer’s own words on public data, the first example of each kind in the set it was built on, so they show what an answer looks like, not how often it is right. That is measured below, on plants it had never seen.

What it does not claim. It is not an intrusion detection system and does not replace one. An attacker who replays old readings, or who moves the neighbouring readings too, is not claimed to be caught. And a person who changes a setting without it being on record can look like an attack; that is why it says “check who changed it”, not “attack”.

Rain, a dead sensor, or an attack.

When a reading leaves what was expected, the person on shift needs to know why. We tested an explainer on public plants it had never seen, and report it pass or fail.

Tested on plants it had never seen

A level jumps. Was it the rain, a sensor that died, someone changing a setpoint, or someone who should not be in the system? We built an explainer that expects each reading from its own past, its neighbours, the rain gauges and the setpoints, and names a cause with its reasons, or says it cannot tell. No language model decides; one may only put the reasons into words. We tested it on 165 deviations on three public plants it had never seen, chosen and locked before the test: two real wastewater networks, in Austria and England, and an industrial security testbed with recorded attacks.

On the two networks it was right on 56 of 98 deviations and wrong on 2; the checks we already ship were right on 62 and wrong on 8, so overall it did not beat them. The difference is in the costliest mistake, blaming a dead sensor on the rain: our checks did that 7 times, the explainer 1 time. Four small local language models, given the same evidence, were wrong on 32 to 93 of every 100.

On the security testbed it hardly answered. Its export came in several files, and our reader kept only the first one of each reading, so on 63 of 67 deviations it saw too little history and said it could not tell. It said attack once, and was right; that is no test of finding attacks. The reader will be fixed and tested on new locked data, not on this.

P54, written down before the held-out run: PARTIAL. Finding attacks: FALSIFIED.

Back to the front page

Start with one export.

We sit with you and run it on a machine you control, so nothing leaves the site. You keep the mapping report: every tag with its quantity, unit and machine and the evidence for each, what could not be told and why, and the data that went wrong this week, in Norwegian or English.

Write to [email protected]. If you would rather send us an anonymised export, we sign your data processing agreement first.

Read next