This fork of the GeoIP Attack Map was adjusted for T-Pot, also introducing new features (i.e. dynamic destination IPs to represent T-Pots), better performance for the Attack Map Server by using aiohttp and asyncio and, since 4.0.0, a fully offline map stack: no CDN, no tile service, no API key.
This geoip attack map visualizer was forked and adjusted to display T-Pot Honeypot events in real time. The data server connects to elasticsearch, parses out source IP, destination IP, source port, destination port, timestamp, honeypot type and honeypot statistics (events per last 1m, 1h, 1d). Protocols are determined via common ports, and the visualizations vary in color based on protocol type while keeping stats regarding top source IPs and countries. Since 4.0.0 an activity shading layer shades countries by observed event volume at world view and crossfades into a density heatmap (built from the same GeoIP coordinates) when zooming in — source-IP geolocation, not attribution, with city/centroid precision at best; a settings toggle turns it off.
The map runs on MapLibre GL JS with a local
PMTiles vector basemap served by the app itself via
HTTP range requests — zero third-party network requests at runtime, enforced by a strict
same-origin CSP and verified by the browser test suite. The basemap artefact
(static/dist/world.pmtiles, ≈ 45 MB, world z0–6, derived from
Protomaps / © OpenStreetMap contributors) is never
committed: it is pinned by URL and SHA-256 in tools/basemap.lock and downloaded from an
immutable GitHub release by tools/fetch_basemap.sh (see docs/BASEMAP.md).
Requirements: a WebGL2-capable browser (MapLibre 6 removed WebGL1 support). Without WebGL2 the map area shows a clear failure notice while the dashboard, live feed and statistics keep working — the data channel never depends on the map.
tools/fetch_basemap.sh --preset dev # world z0-4 extract of the pinned artefact (a few MB)
python3 AttackMapServer.py --demo # deterministic synthetic events
# open http://127.0.0.1:64299Demo flags: --demo-seed N (default 42), --demo-rate R (events/s, default 2),
--demo-burst N, --demo-scenario basic|antimeridian|single-location|flood.
The server listens on 127.0.0.1 by default (--host); the T-Pot container starts it
with --host 0.0.0.0 explicitly so nginx can reach it on the docker network.
Demo mode is CLI-only, never a default and never enabled via environment variable; every
demo message carries demo: true and the UI shows a DEMO DATA badge. Never run demo mode in
production.
Full-chain test against a real Redis pubsub instead of --demo:
docker run --rm -p 6379:6379 redis:8.4.6-alpine
python3 AttackMapServer.py --redis-url redis://127.0.0.1:6379
python3 -m demo_events --publish-redis redis://127.0.0.1:6379 --demo-rate 5 --demo-seed 42tools/check_all.sh --bootstrap # once per clone; the only mode that downloads tooling
tools/fetch_basemap.sh --preset dev
tools/check_all.sh # full local suite, offline (unit, vendor, browser smoke test)
tools/check_all.sh --release # additionally verifies the pinned full artefact| Artefact | Command |
|---|---|
static/styles/{dark,light}.json |
node tools/styles/generate_styles.mjs |
static/data/countries.geojson(.gz) |
node tools/vendor_countries.mjs --rebuild |
static/vendor.lock |
tools/vendor_frontend.sh --write-lock |
SRI hashes in index.html |
python3 update_hashes.py |
| Vendored engine/assets/licences | tools/vendor_frontend.sh --engine --basemap-assets --licenses |
Generated artefacts are produced under Node.js 24.20.0 LTS (.node-version); details in
docs/UPDATE_HASHES_README.md.
The original attack map was created by Matthew Clark May.
First T-Pot based fork was released by Eddie4.
All notices ship with the app under static/licenses/:
MapLibre GL JS,
PMTiles,
fflate,
Protomaps basemaps (styles) and
basemap tiles © OpenStreetMap contributors (ODbL),
sprite icons derived from MIT-licensed tangrams/icons,
Noto Sans glyphs (OFL 1.1),
Natural Earth (public domain, country geometry),
Bootstrap,
Chart.js,
Flagpack,
Font Awesome,
Inter,
JetBrains Mono.
