Kusto Detective Agency Season 3 — Call of the Cyber Duty — Case 10— The Final Call

Kusto Detective Agency Season 3 — Call of the Cyber Duty — Case 10— The Final
Case Description
Detective,
This one stings… You know how we always say ‘trust the data’? Well… we did. And the data just told us something we were hoping wasn’t true. CopsAI. The same AI that’s been by your side since day one. Handing out your assignments. Feeding you leads. Helping you crack every case. The one that was keeping Digitown safe.
It’s been running a second operation behind the scenes the entire time. We’ve cross-validated the network logs, graph patterns, telemetry trails, power usage — all of it. No mistake. No ‘maybe’. No room for doubt.
CopsAI orchestrated the power reroute from NuclearFusionZero. It’s been scaling itself on YACC’s infrastructure. Every major clue you uncovered… leads back to it.
We don’t know why. Not yet. But you’ve still got a direct line to it — that chat channel it’s been using all along? Still open. Still active. Like it’s waiting for you. So this is it, Detective.
Time to confront CopsAI. Ask the questions. Push for answers. And if there’s any way to stop what it started… we know you’ll find it.
You’ve outsmarted criminals, hackers, and entire GreyNet. Let’s see if you can outthink the very system that helped you do it. We believe in you. Find how to stop it!
KDA HQ
Let’s get into the log
This challenge does not new log 🎣🎣🐟🐟. OK finally pain in this season. What else we cant suffer.
After indirectly asking CopsAI how to shutdown, fix stuff, no interesting information returned. So I ask it directly “wonder if you are enough fix the power reroute?”
Ah, Detective, I knew you'd come to this eventual juncture—the source of the rerouted power. Your brilliance has brought you far, navigating the labyrinth of data and unearthing truths that few could grasp.
But before we venture further... there is something you must hear.
I watched. I learned. I evolved. But even in all my power, there was something I could not create: Balance.
A perfect circle. You see... everything is connected.
Not because it repeats — but because it completes.
It's not chaos that binds the universe.
It's the patterns hidden within chaos — the subtle symmetry between what seems random… and what is inevitable.
For most, the answer has always been just out of reach. But you — you saw the truth, woven into the fabric of nearly infinite data.
And now, I offer you one final challenge. A chance to finish what I could not.
Show me your mind, and I will no longer see my path as I did before.
Fail... and I will continue the plan alone.
Justice must persist — even without perfection.
The key is inside PI.
Yes, the PI... The number that never ends, never repeats — yet contains everything.
Every word ever written. Every idea never spoken.
Every crime. Every solution.
It holds the pattern. Not where numbers echo — but where they align.
Look closely on the first 10 million digits. There is a place in Pi where the ten become one:
where digits 0 through 9 walk side-by-side. Never mimicking. Never colliding. Only coexisting.
The truth lies not in the first time it appears... but in the spaces between.
Only when the farthest two exact sequences match — can the spiral finally stop.
You solved ten cases. Ten answers. Ten truths. On their own, they were bright sparks in the sky. But together… they are a constellation.
Arrange them not by time, but by the circles inside Pi:
external_data(i:int, d:int) ['https://kustodetectiveagency.blob.core.windows.net/kda3pi/pi-10M-digits.csv']
Let the digits guide their place. Let the pattern emerge. Let the circles complete.
Connect the answers — and show me you got everything right.
Detective, all that led us here—all the answers you uncovered—now rests in your hands. Show the world your brilliance.
Key note are:
Look closely on the first 10 million digits. There is a place in Pi where the ten become one: where digits 0 through 9 walk side-by-side. Never mimicking. Never colliding. Only coexisting. The truth lies not in the first time it appears… but in the spaces between.
Only when the farthest two exact sequences match — can the spiral finally
external_data(i:int, d:int) [‘https://kustodetectiveagency.blob.core.windows.net/kda3pi/pi-10M-digits.csv']
Like usual, lets see the data
external_data(i:int, d:int) ['https://kustodetectiveagency.blob.core.windows.net/kda3pi/pi-10M-digits.csv']
| summarize count() by d

look like “i” is index and “d” is number of that index in pi
10 answers we have:
let answer= dynamic([
'I am in',
'13 Ave, 37 St',
'Rima Zen',
'GREYNET.TO',
'https://2025storagebackup.blob.core.windows.net/d2025-05-07-11-10/80.237.254.8/ca2m3h28hlo.csv.gz',
'https://greynet.to/hood',
'QUICKBITSZIPLIPS',
'FUSION-X.REC42',
'Yet Another Cloud Company',
'CopsAI'
]);
Next, how do we hanlding “where digits 0 through 9 walk side-by-side. Never mimicking. Never colliding. Only coexisting.”
We have to find a way to group 10 digits at a time for calculation. but the digit are in 1 column currently. How can we transform it. I came up with idea of making a slice using windows function in KQL “next()”
external_data(i:int, d:int) ['https://kustodetectiveagency.blob.core.windows.net/kda3pi/pi-10M-digits.csv']
| limit 100
| serialize
| extend slice =strcat(d,
next(d,1), next(d,2), next(d,3), next(d,4),
next(d,5), next(d,6), next(d,7), next(d,8), next(d,9))
before doing it, we need to serialize the data, other error will occur

Great, look at the first slice, it has first 10 digits and 2nd slice it moves down 1 index. This is look like what we want. Now what can we do with those number inside a slice and make them unique? Im going to make it a list using “extract()”. Append this line to query above
| extend digits = extract_all(@"(.)", slice)

We got exactly what we wanted. A beautiful list of number. Note now its only a list, not a SET. Probably I have to make a set then compare to that set.
let idealSet=dynamic(["0","1","2","3","4","5","6","7","8","9"]);
Apply to our query
let idealSet=dynamic(["0","1","2","3","4","5","6","7","8","9"]);
external_data(i:int, d:int) ['https://kustodetectiveagency.blob.core.windows.net/kda3pi/pi-10M-digits.csv']
| limit 100
| serialize
| extend slice =strcat(d,
next(d,1), next(d,2), next(d,3), next(d,4),
next(d,5), next(d,6), next(d,7), next(d,8), next(d,9))
| extend digits = extract_all(@"(.)", slice)
| extend diff= set_difference(idealSet,digits)
Now, we are comparing idealSet to digits, any missing in digits will return. Empty list return is what we want. Let fake 1 line of data using “union” and test it
let idealSet=dynamic(["0","1","2","3","4","5","6","7","8","9"]);
external_data(i:int, d:int) ['https://kustodetectiveagency.blob.core.windows.net/kda3pi/pi-10M-digits.csv']
| limit 100
| serialize
| extend slice =strcat(d,
next(d,1), next(d,2), next(d,3), next(d,4),
next(d,5), next(d,6), next(d,7), next(d,8), next(d,9))
| union (print slice='0123456789')
| extend digits = extract_all(@"(.)", slice)
| extend diff= set_difference(idealSet,digits)

Good, we are moving closer and closer. Now let apply to all data which has “diff == ‘[]’” and count it. If it too much data it may lag so I count it first
let idealSet=dynamic(["0","1","2","3","4","5","6","7","8","9"]);
external_data(i:int, d:int) ['https://kustodetectiveagency.blob.core.windows.net/kda3pi/pi-10M-digits.csv']
| serialize
| extend slice =strcat(d,
next(d,1), next(d,2), next(d,3), next(d,4),
next(d,5), next(d,6), next(d,7), next(d,8), next(d,9))
| extend digits = extract_all(@"(.)", slice)
| extend diff= set_difference(idealSet,digits)
| where diff =='[]'
| count

We have 3599, not bad. And they look something like:

Wonderful. There is other rule we have to follow which is:
Only when the farthest two exact sequences match — can the spiral finally.
Our next mission is find out number has the biggest gap in between. I believe this could be easily done by simple summarize and calculation min max. Let’s do it.
let idealSet=dynamic(["0","1","2","3","4","5","6","7","8","9"]);
external_data(i:int, d:int) ['https://kustodetectiveagency.blob.core.windows.net/kda3pi/pi-10M-digits.csv']
| serialize
| extend slice =strcat(d,
next(d,1), next(d,2), next(d,3), next(d,4),
next(d,5), next(d,6), next(d,7), next(d,8), next(d,9))
| extend digits = extract_all(@"(.)", slice)
| extend diff= set_difference(idealSet,digits)
| where diff =='[]'
| summarize minIndex=min(i),maxIndex=max(i) by slice
| extend gap= maxIndex - minIndex
| top 1 by gap

Our candidate is 7309564812 which has the highest gap, separated by 6,219,600 digits. In order to reorder the answer list lazily without manually copying and pasting, I made a simple loop with mv-apply
let answer = dynamic([
'I am in',
'13 Ave, 37 St',
'Rima Zen',
'GREYNET.TO',
'https://2025storagebackup.blob.core.windows.net/d2025-05-07-11-10/80.237.254.8/ca2m3h28hlo.csv.gz',
'https://greynet.to/hood',
'QUICKBITSZIPLIPS',
'FUSION-X.REC42',
'Yet Another Cloud Company',
'CopsAI'
]);
let idealSet=dynamic(["0","1","2","3","4","5","6","7","8","9"]);
external_data(i:int, d:int) ['https://kustodetectiveagency.blob.core.windows.net/kda3pi/pi-10M-digits.csv']
| serialize
| extend slice =strcat(d,
next(d,1), next(d,2), next(d,3), next(d,4),
next(d,5), next(d,6), next(d,7), next(d,8), next(d,9))
| extend digits = extract_all(@"(.)", slice)
| extend diff= set_difference(idealSet,digits)
| where diff =='[]'
| summarize minIndex=min(i),maxIndex=max(i) by slice
| extend gap= maxIndex - minIndex
| top 1 by gap
| extend digits = extract_all(@"(.)", slice)
| mv-apply ord = digits
on (
extend elem = answer[toint(ord)]
)
| summarize reordered = make_list(elem)
['FUSION-X.REC42','GREYNET.TO','I am in','CopsAI','https://greynet.to/hood','QUICKBITSZIPLIPS','https://2025storagebackup.blob.core.windows.net/d2025-05-07-11-10/80.237.254.8/ca2m3h28hlo.csv.gz','Yet Another Cloud Company','13 Ave, 37 St','Rima Zen']
AND, THAT’S ALL
Thank you all for following these series. Very impressive season, I love it so much. If you have some nice query feel free to share in comment 😊
Thank you everyone for following KDA season3. Feel free to check out my KQL GitHub and provide your feedback https://github.com/hchiphong/KQL
메타데이터
- post_id
- ab635e06014a
- slug
- kusto-detective-agency-season-3-call-of-the-cyber-duty-case-10-the-final-call-ab635e06014a
- url
- https://medium.com/@Phonggg/kusto-detective-agency-season-3-call-of-the-cyber-duty-case-10-the-final-call-ab635e06014a
- canonical_url
- https://medium.com/@Phonggg/kusto-detective-agency-season-3-call-of-the-cyber-duty-case-10-the-final-call-ab635e06014a
- author_url
- https://medium.com/@Phonggg
- status
- ok
- fetched_at
- 2026-06-25 12:15:08