Context & Problem
In September 2023 a man in Mbalenhle, Mpumalanga, parked his Isuzu bakkie at home. When he woke up on the morning of Friday 1 September it was gone. A tip from a resident and his tracking company led police to a yard in Tsakane, across the provincial border in Gauteng, where they found the bakkie tampered with, IOL reported. He was lucky.
Two years later thieves taking bakkies from homes in Springs and Brakpan were striking between 01:00 and 04:00 and poisoning the dogs so they would not wake the owners, and a security company there said the bakkies' trackers "can be removed effortlessly", the Brakpan Herald reported. By the time an owner wakes up, the car may be far away and its tracker gone.
In the year to March 2026 the police recorded 17,464 carjackings in South Africa, about 48 a day, and 29,671 vehicles stolen with nobody in them, about 81 a day. Both have fallen in the past few years: carjackings by 23% from 22,808 in 2023/24, and vehicle thefts by 21% from 37,426 in 2022/23. That still leaves about 130 vehicles taken every day, and the counts hold only the crimes reported to the police.
A tracker is meant to help get a stolen vehicle back. It reports where the vehicle is and what its unit senses, and the tracking company raises an alert when the unit is jammed, loses power or crosses a boundary the owner has drawn. Those alerts are blunt. Most odd trips are someone running late or a unit misbehaving, so an alert on each one is soon ignored. A theft that looks like an ordinary late drive raises none, and once a thief has found and removed the unit, as in Springs and Brakpan, there is nothing left to report.
I wanted something that watches the feed the way someone who knows the vehicle would: something that can tell an owner running late from a vehicle being driven away, and that does not lose interest when the tracker goes quiet.
What I built
What I ended up with is Vigil, an engine that sits on a tracker's live feed and decides, every minute, whether anything should be done about each vehicle. It is a personal project, and it comes with a fleet dashboard and a mobile app.
Vigil keeps a running belief about what each vehicle is doing, and what it does follows that belief. Most of the time that means nothing at all. When the belief turns, it escalates, and the demo below shows what that looks like when a bakkie's tracker falls silent on the highway.
Tipo Couriers
Owner · fleet account
Live
| Vehicle | Status | Health | Speed | Where | Last report |
|---|---|---|---|---|---|
| Van 01 | Driving | Good | 36 km/h | Kempton Park | Just now |
| Van 02 | Parked | Good | 0 km/h | Kempton Park | 5 min ago |
| Bakkie 03 | Driving | Good | 183 km/h | Germiston | Just now |
| Truck 04 | Parked | Good | 0 km/h | Germiston | 13 min ago |
| Truck 05 | Parked | Good | 0 km/h | Boksburg | 3 h ago |
| Van 06 | Parked | Good | 0 km/h | Edenvale | 8 min ago |
How I built it
Vigil is written in Python. A timer runs it in the cloud once a minute, and each run collects the latest data from the tracking units of every connected organisation through their telemetry provider, keeps what has changed since the last read and passes each vehicle's new reports to the engine. The engine works from three things: what is normal for the vehicle, learned from its own history; how likely vehicle crime is where and when the vehicle is, from a model of police statistics; and the health of its tracking unit. The fleet dashboard is a web page, the mobile app is written in SwiftUI, and 538 automated tests cover the engine, the accounts and the connection to the telemetry provider.
Each part was tested before I relied on it. The crime model was fitted on public data and tested on precincts and years it had not seen. The engine was tested by replaying a test fleet's real history minute by minute, with thefts staged in it. Both tests are described below.
The data
Vigil reads two kinds of data. The first is the data from the tracking unit fitted in each car. The unit sends a report whenever something changes and, while the car moves, about every 30 seconds. The telemetry provider passes the reports on, and Vigil collects them once a minute for every vehicle. There is no way to ask only for what is new, so each collection looks back over the last half hour and Vigil keeps each report once.
The two times on a report matter more than they look. A unit that loses signal, in a basement or out of network, keeps its readings and sends them in a batch when it reconnects, sometimes hours later. A report is of use only from the moment it is received, and the tests below hold Vigil to that.
When an organisation connects its tracker, Vigil imports the last 90 days of its history, about ten days' worth a minute, and learns from it before it starts watching. The test fleet's full history holds 2,273 trips, and 346 of them, about 15%, start at night. That is why a trip at night cannot be enough to raise an alarm on its own.
The second kind is public: the police's crime statistics, the census and OpenStreetMap, which go into the crime model in the next section. Nothing from the tracker goes into the crime model. The charts on this page use public data or made-up values, and the test results are counts from the test fleet, with no positions or places.
Where cars are taken
Vigil starts from where vehicle crime happens. The police publish monthly counts for each of the country's 1,167 police precincts, and Vigil uses four of their crimes from January 2020 to March 2026: carjacking, vehicle theft, theft from vehicles and truck hijacking. The raw counts are a poor guide on their own. A busy precinct with many cars records many thefts without being riskier for any one car, and 444 precincts recorded no carjacking at all in 2024/25, which does not mean a carjacking there is impossible.
The monthly counts up to September 2025 come from crime-stats, an open project that gathers the police's releases, and the six months after that from the police's quarterly workbooks. Twelve stations opened during the period. Until each one opened, its area's crimes were recorded at an older station next door, so in those months both stations are left for the model to fill, and the combined count the older station recorded is kept back to check the fill. What is known about each precinct comes from two places. OpenStreetMap gives its roads, fuel stations, shops, land use and distance to a border post. Census 2022 gives the share of households with a car and the kinds of dwelling they live in, and Stats SA tabulates the census by police district, so the figures are exported per precinct from its SuperWEB tool with no map overlay needed. I also tried the number of swimming pools as a sign of wealth and dropped it: OpenStreetMap shows 33,110 pools in the Western Cape against 3,405 in Gauteng and 29 in Sandton, which says more about who maps than about who is wealthy.
The model is a Bayesian count model, fitted separately for each crime with R-INLA. A precinct's monthly count depends on what is known about the precinct, a spatial effect that lets neighbouring precincts share strength, its own trend, national and provincial trends, the calendar month, and a precinct-by-year effect that lets part of a bad year carry into the next. The model turns the counts into a risk per car, and the map compares that risk with the national rate.
Time matters as much as place. Carjacking peaks in the early evening: 30.7% happen between 18:00 and 21:00, more than twice an even share. Fridays and Saturdays carry about 18% each. The police last published time-of-day tables for 2016/17 to 2019/20, and most give only the peak hours or days for one province, so I combined 83 such shares for the hours and 149 for the days with a Gibbs sampler that fills in what each report left out.
To test the map, 117 precincts were hidden from the model, which then had to fill them in from their neighbours and their census and map data alone. Its estimates were closer to the recorded counts than the national rate for every crime, and closer than the neighbours' rate for all but carjacking, where the two are too close to separate: carjacking hotspots cluster so tightly that copying the rate next door works well.
What Vigil needs from the model is a forecast: a precinct's risk in the months ahead from its record so far. So I hid the last year of every precinct, refitted the model on the months before and scored its forecast, then did the same for each of the two years before that. The model beat the rule of repeating last year's count in 9 of 12 crime-years and drew level in the other three, all in 2023/24. The trend and the year effect stayed in only because they won these tests. Without them the model's error was higher in 11 of the 12 crime-years, and above last year's count for theft from vehicles in all three.
The precincts that record nothing are the reason the model exists. Of the 444 precincts with no carjacking in 2024/25, 68 recorded at least one the next year, 103 carjackings in all, against 121 the model expected. Among those zero precincts, the model's riskiest quarter went on to record one 40% of the time, against 2% for its safest quarter. A few zeros look more like a change in recording than a change in crime: Harrismith, Hout Bay and Madadeni went from 4 to 11 carjackings a year to none in 2025, and the model takes that as a real fall.
All of this is crime recorded by the police, and much goes unrecorded. Stats SA's victim survey for 2025/26 estimates about 87,000 vehicle thefts and 122,000 hijackings of households in a year, against about 30,000 and 17,500 recorded, though the survey counts households and uses a wider meaning of hijacking. The map shows where crime is relatively likely more than how much there is.
How it decides
A theft rarely looks strange in itself. A car leaving at 02:00 is odd for one owner and ordinary for a nurse on night shift, so Vigil judges every vehicle against its own history and keeps up as habits change. It learns the tracking unit as well, and keeps an eye on its health, because a worn unit can look like a theft and a theft can hide behind a worn unit.
From all of this Vigil keeps a running belief about what each vehicle is doing, and most of the time that belief is that nothing is wrong. What it does follows the belief, weighed against what it costs to be wrong either way: a false alarm wears out the people who answer it, and a missed theft can cost a vehicle. As the belief that something is wrong grows, Vigil escalates.
How it runs
Vigil's customer is an organisation: a company with a fleet, or an owner with one car, who is an organisation of one. A person can belong to several organisations and sees only their side of each. Owners and admins see their whole organisation, fleet managers the vehicle groups they are given, and drivers their own trips and the questions about the vehicle they drive.
An organisation joins by connecting its account with its telemetry provider. Vigil checks the account's details with the provider before saving them, stores them encrypted, and never shows or logs them again. It then imports the history and keeps each vehicle in learning mode until it has seen about 30 of its trips, and the dashboard shows which vehicles are still learning and how many trips each has been learned from. Owners and managers can also tell Vigil what it cannot learn on its own: depots, shift hours, which drivers may drive which vehicle, and the boundaries they want watched. Disconnecting the provider deletes everything Vigil kept for that organisation.
At the end of each day Vigil grades every vehicle's tracking unit from that day's readings: whether it reported as it should, kept its GPS fix, and held its batteries and ignition reading. When a part looks faulty the owner is told once which part it is, with a reminder each week while it stays faulty. A failing unit is not an emergency, but it is worth fixing, because a worn unit makes a theft harder to see.
Each organisation costs one run a minute, 43,200 a month, whatever the size of its fleet. On the cloud provider's published prices, the free monthly allowance covers about a dozen organisations, and each one after that costs about 53 US cents a month, with storage at about a cent per vehicle. So a fleet of 100 vehicles costs about 1.5 cents a vehicle, while an owner with one car costs over 50 cents, and the price will have to follow that.
How it was tested
Vigil was tested on a test fleet's own history, and the rules of the test were set before it ran. Vigil learned from the trips before 1 September 2026 and was then run over the 511 trips after it, minute by minute as if live. It saw each report only from the moment it had been received, not the moment it was taken, because a unit that goes quiet and later sends its readings in a batch looks very different in the moment than in hindsight.
Three targets were set first: at most about one question per vehicle a month on ordinary trips, at most about one false escalation per 100 vehicles a month, and clear incidents caught within 5 minutes. All 511 trips were ordinary, so any question or alert would have been a false alarm. Vigil raised none. That is a good result on a small test. The 511 trips are about 3.4 vehicle-months of driving, and no question in 3.4 vehicle-months is consistent with as many as 0.9 a vehicle-month at 95% confidence, so the test only just reaches the first target and is far too small to check the second.
The history holds no thefts, so they were staged: made-up reports written into the real history, 30 of each kind. A jammed tracker was caught in all 30 cases, a median 3 minutes in, inside the 5-minute target. A stripped unit was caught in 29 of 30, a median 6 minutes in, a minute outside it, and a towed car in 29 of 30.
These are the results of a second run, made after a daily grade for each tracking unit was added. Run without the grading, Vigil sent 2 fault notes on the ordinary trips, judging healthy units faulty. With it, Vigil sent none and caught 29 stripped units within the hour instead of 27. The towed cars paid for it: they were still caught within the hour, but only 17 within 15 minutes against 26 before, because one car's unit had a doubtful reading on 265 of 312 days and a tow shows partly in that reading. The grading stayed in, because a fault note on a healthy unit is the kind of alarm people learn to ignore.
How strongly a theft shows in the reports is an assumption, because the history has no real thefts to measure it from. Halving or doubling those assumptions moves the stripped-unit catches from 13 to 30 of 30, so they matter, and the owners' answers to Vigil's questions are what could pin them down.
One idea was tested and left out. The trackers' own harsh-driving fields were empty, so I had Vigil work out harsh braking, acceleration, turning, speeding and hard launches itself, and compare each trip with the car's usual style. It caught no more thefts. Against a style for the whole car it fired mostly on the harder of the two people who drive it, and its apparent power to tell drivers apart, an AUC of 0.81 to 0.91, came mostly from the roads each of them used: trained on other roads, it recognised one driver in only 2 of 11 trips on the other's roads. A signal that mostly learns who drives where would raise false alarms without catching more thefts, so it stayed out.
Limitations
The incident side is assumed. The staged thefts follow the engine's own idea of what a theft looks like, so the tests check the engine against that idea as much as against thieves. The ordinary side is real but small: no false alarm in 511 trips is consistent with as many as about 1 in 170 trips drawing one, at 95% confidence.
Not every theft can be told apart from ordinary driving from the tracker alone. The crime risk is per police precinct, so a whole precinct shares one rate, and it rests on crimes reported to the police, which leave out many.
Only one telemetry provider has been tested, and questions reach the mobile app without push notifications for now, so an answer can be slow. The running cost is an estimate from an assumed run time and has not yet been checked against a bill.