Geo Detection
This page explains how the GeoLite2 databases are enabled, how IP locations are cached, and the conditions for the four geo anomaly checks, including impossible travel.
Enabling
| Setting | Result |
|---|---|
Both Filepath.CityDB and Filepath.CountryDB empty |
No GeoLite2 instance; geo detection off |
| Both fail to open | Logs a warning; geo detection off |
Only CountryDB opens |
calcGeo requires CityDB, so geo detection is skipped entirely |
CityDB opens |
On; CountryDB is only a fallback when the City lookup fails |
Download the database (GeoLite2-City.mmdb) from MaxMind yourself; this library does not update it.
Lookup and History
| Step | Behavior |
|---|---|
| Private IP | Skips the database and returns an empty location (no country or city, coordinates 0) |
| Cache | geo:ip:{ip} keeps the lookup result for 24 hours |
| Not found | Logged and skipped; the request's geo score is 0 |
| History | geo:locations:{sid} pushes {ms}:{country code}:{city}:{lat}:{lng}, keeps the last 10, TTL 24 hours |
Four Checks
| Flag | Condition | Score |
|---|---|---|
geo_high_risk |
History contains a country code listed in HighRiskCountry |
ScoreGeoHighRisk (30) |
geo_hopping |
More than 4 distinct countries within the last hour | ScoreGeoHopping (15) |
geo_frequent_switching |
Within the last hour, >= 4 cities, >= 5 records, and more than 4 city switches between adjacent records |
ScoreGeoFrequentSwitch (20) |
rapid_geo_change |
The two newest records are under an hour apart and the implied speed is > 800 km/h, or the move is > 500 km within 30 minutes |
ScoreGeoRapidChange (25) |
geo_high_risk currently never fires because of a field mismatch; see Known Issues.
Impossible Travel
Distance uses the haversine formula with an Earth radius of 6371 km, and speed is distance divided by the time gap. 800 km/h is roughly airliner cruising speed, which normal movement never exceeds, while VPN node switches and rotating proxy pools do.
| Example | Distance | Gap | Result |
|---|---|---|---|
| Taipei → Tokyo | about 2,100 km | 10 minutes | Fires (both the speed and 30-minute rules hold) |
| Taipei → Taichung | about 140 km | 20 minutes | Does not fire (420 km/h) |
| Taipei → Tokyo | about 2,100 km | 3 hours | Not compared (over one hour) |
Boundaries
| Case | Behavior |
|---|---|
| One session alternating between private and public IPs | Private records sit at (0, 0), so switching to a public IP looks like a jump of thousands of kilometers from the Atlantic and may trigger rapid_geo_change |
| GeoLite2 accuracy | City-level locations can be off by tens of kilometers; different IPs in one city only affect the city-switch count |
| Debug output | calcGeo prints the location string through the standard log.Print on every request; see Known Issues |