
Open-Source-Tracker für Schattenflotten in der Ostsee. 1200+ Schiffe, Live-AIS, Kabelnähewarnungen. Keine Cloud, kein Abonnement, läuft lokal.
Überwachung der Schattenflotte in der Ostsee mittels Live-AIS-Daten. Beobachtet über 1.200 Schiffe aus dem ukrainischen GUR-Katalog für Krieg und Sanktionen über den AISStream-WebSocket-Feed, zeichnet Positionen auf einer sich selbst aktualisierenden Karte ein, markiert die Nähe zu Unterseekabeln und erkennt Russland↔Westen-Umschlagsmuster.
Kostenlos, Open Source, läuft lokal. Keine Cloud, kein Abonnement außer einem kostenlosen AISStream-API-Schlüssel.
Teil der Former Lab-Werkzeugkette für souveräne Aufklärung.
Shadow Fleet Tracker Light wird vom Former Lab-Team entwickelt und gewartet – souveränes Computing, das offene, datenschutzorientierte Werkzeuge auf alter Hardware ohne VC-Finanzierung baut.
Wenn dieses Werkzeug für Sie nützlich ist, unterstützen Sie uns auf Patreon. Eine kostenlose Stufe ist verfügbar, mit einer 7-tägigen Testphase auf den kostenpflichtigen Stufen. Unterstützen Sie immer die Ukraine!
git clone https://github.com/FormerLab/shadow-fleet-tracker-light.git
cd shadow-fleet-tracker-light
pip install -r requirements.txt
export AISSTREAM_API_KEY=your_key_here # kostenlos bei aisstream.io
python shadow_tracker.py &
uvicorn webserver:app --host 0.0.0.0 --port 8000
Öffnen Sie http://localhost:8000 – die Karte ist unter /map erreichbar.
Windows-Benutzer: Siehe Abschnitt Setup für die Syntax der Umgebungsvariablen in PowerShell/cmd.
Linux-/macOS-Benutzer können start.sh verwenden. Windows-Benutzer können start.bat oder start.ps1 verwenden – beide öffnen Tracker und Dashboard in separaten Fenstern und starten den Browser automatisch.
Vessels1.db) stammenfiltered_cables.kml)Dunkle CartoDB-Karte mit eingezeichneten Kabelrouten in Grün. Schiffsmarkierungen aktualisieren sich alle 3 Minuten mit einem Live-Countdown bis zur nächsten Aktualisierung – die Seite lädt automatisch neu. Popups zeigen MMSI, IMO, Flagge, Geschwindigkeit, Kurs, Ziel und Deep-Links zu MarineTraffic, VesselFinder und War&Sanctions an. Offener Sanktionsstatus wird angezeigt, wenn ein API-Schlüssel konfiguriert ist.
Bekannte Schiffsnamen aus Vessels1.db werden sofort beim ersten Erscheinen angezeigt, ohne auf eine AIS-Statiknachricht zu warten.
Beim Start wird die Karte mit den letzten 24 Stunden des Positionsprotokolls vorbelegt. Schiffe, die in der aktuellen Sitzung noch nicht gesehen wurden, werden als graue Markierungen mit einem „zuletzt gesehen vor Xh“-Label angezeigt – sie wechseln zu Live-Farben, sobald neue Pings eintreffen.
shadow_tracker.py Hauptprozess – WS-Consumer, Kartenrenderer, DB-Schreiboperationen
loitering_module.py Trödelerkennung und Kartenannotation
transshipment_module.py Hafenanruferkennung und Markierung von Russland↔Westen-Umschlägen
webserver.py FastAPI-Dashboard
gur_scrape.py Einmaliger Scraper – erstellt IMO→GUR-ID-Mapping + vollständigen Schiffskatalog
check.py Vorabprüfungen – Python-Version, Abhängigkeiten, API-Schlüssel, Netzwerk
start.sh Startprogramm für Linux / macOS
start.bat Startprogramm für Windows (Eingabeaufforderung)
start.ps1 Startprogramm für Windows (PowerShell)
requirements.txt Abhängigkeiten
Vessels1.db Beobachtungsliste – über 1.200 Schiffe (MMSI + IMO + Name, wo bekannt)
vessel_data_log.db Positionsprotokoll – Laufzeit, nur Anhängen
vessel_static.db AIS-Statikdaten-Cache (Name, Ziel, Flaggenverlauf)
transshipment.db Hafenanrufprotokoll und Umschlagereignisse
loitering_events.db Trödelereignisse
filtered_cables.kml Geometrie der Ostsee-Unterseekabel
cables.kml Vollständiger Kabeldatensatz (Vorfilterquelle)
gur_mapping.json IMO→GUR-ID-Mapping (erzeugt von gur_scrape.py, optional)
gur_vessels_full.json Vollständiger GUR-Katalog – GUR-ID→{imo, mmsi, name, flag} (erzeugt von gur_scrape.py)
Vessels1.db ist die kanonische Liste der verfolgten Schiffe. Über 1.200 Einträge aus dem ukrainischen GUR-Katalog für Krieg und Sanktionen, die Tanker und Frachtschiffe abdecken, die als Teil der russischen Schattenflotte identifiziert oder an hybrider Aktivität in der Ostsee beteiligt sind.
Die Liste enthält kürzlich von den baltischen Behörden beschlagnahmte oder abgefangene Schiffe:
MMSIs werden anhand aktueller AIS-Daten korrigiert – Schiffe der Schattenflotte wechseln häufig die Flagge. Die Datei update_vessels_2026_03.sql dokumentiert alle Änderungen mit Quellen.
Die Beobachtungsliste ist bewusst offen. Fügen Sie Schiffe direkt in Vessels1.db hinzu, während der Tracker läuft – sie werden innerhalb von 5 Minuten ohne Neustart übernommen.
Linux / macOS:
sqlite3 Vessels1.db "INSERT OR IGNORE INTO vessels (mmsi, imo, name) VALUES ('123456789', '9999999', 'SCHIFFSNAME');"
Windows (kein sqlite3-CLI erforderlich):
import sqlite3
conn = sqlite3.connect("Vessels1.db")
conn.execute("INSERT OR IGNORE INTO vessels (mmsi, imo, name) VALUES ('123456789', '9999999', 'SCHIFFSNAME')")
conn.commit()
conn.close()
Vessels1.db — Beobachtungsliste
vessels(mmsi TEXT PK, imo TEXT, name TEXT, destination TEXT)
vessel_data_log.db — Positionsprotokoll
vessel_data_log(timestamp, mmsi, name, imo, destination, speed, heading, latitude, longitude, cable_alert)
Nur Anhängen. Wird bei jedem Kartenrenderzyklus geschrieben.
vessel_static.db — AIS-Statikdaten-Cache
vessel_static(mmsi TEXT PK, name TEXT, imo TEXT, destination TEXT, flag TEXT)
flag_history(id, mmsi, flag, timestamp)
Wird aus Live-ShipStaticData-AIS-Nachrichten befüllt. Überschreibt Namen aus Vessels1.db, sobald empfangen. flag_history zeichnet jede Flaggenänderung mit Zeitstempel auf – Neubeflaggungsereignisse werden protokolliert und auf der schiffsbezogenen Seite angezeigt.
loitering_events.db — Trödelprotokoll (wird zur Laufzeit erstellt)
loitering_events(id, mmsi, timestamp, latitude, longitude, near_cable)
transshipment.db — Hafenanruf- und Umschlagsprotokoll (wird zur Laufzeit erstellt)
port_calls(id, mmsi, name, port, port_type, entry_ts, exit_ts, min_speed)
transshipment_events(id, mmsi, name, direction, from_port, from_exit_ts,
to_port, to_entry_ts, days_between, detected_ts)
requirements.txtDer einfachste Weg, den Tracker auszuführen. Jedes Skript installiert Abhängigkeiten, führt Vorabprüfungen durch und startet beide Prozesse.
Linux / macOS:
chmod +x start.sh
export AISSTREAM_API_KEY=your_key_here
./start.sh
Windows (PowerShell):
$env:AISSTREAM_API_KEY = "your_key_here"
.\start.ps1
Windows (Eingabeaufforderung):
set AISSTREAM_API_KEY=your_key_here
start.bat
Alle drei Skripte führen zuerst check.py aus – eine Vorabprüfung, die Python-Version, Abhängigkeiten, Datendateien, API-Schlüssel und Netzwerkerreichbarkeit überprüft, mit klaren Fehlermeldungen, falls etwas fehlt.
Sie können die Vorabprüfung auch jederzeit eigenständig ausführen:
python check.py
1. Repository klonen und Abhängigkeiten installieren
git clone https://github.com/FormerLab/shadow-fleet-tracker-light.git
cd shadow-fleet-tracker-light
pip install -r requirements.txt
Auf Ubuntu 22.04+ und Debian 12+ kann pip mit einer „externally managed environment“-Fehlermeldung abbrechen. Die Startskripte erstellen automatisch eine virtuelle Umgebung – führen Sie einfach start.sh aus, und es kümmert sich darum. Bei manueller Installation erstellen Sie zuerst ein venv:
python3 -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
pip install -r requirements.txt
2. AISStream-API-Schlüssel setzen
Linux / macOS:
export AISSTREAM_API_KEY=your_key_here
export OPENSANCTIONS_API_KEY=your_key_here # optional
Windows (Eingabeaufforderung):
set AISSTREAM_API_KEY=your_key_here
set OPENSANCTIONS_API_KEY=your_key_here
Windows (PowerShell):
$env:AISSTREAM_API_KEY = "your_key_here"
$env:OPENSANCTIONS_API_KEY = "your_key_here"
3. Tracker und Dashboard in zwei separaten Terminals ausführen
Terminal 1 – der AIS-Feed-Consumer:
python shadow_tracker.py
Terminal 2 – das Web-Dashboard:
uvicorn webserver:app --host 0.0.0.0 --port 8000
4. Dashboard öffnen
http://localhost:8000
Die Live-Karte ist unter http://localhost:8000/map und aktualisiert sich alle 3 Minuten. Beim ersten Start rendert die Karte sofort aus dem vorhandenen Positionsverlauf in der Datenbank – kein Warten auf eine leere Karte.
| Dienst | Erforderlich | Kostenlose Stufe | Link |
|---|---|---|---|
| AISStream | Ja | Ja – unbegrenzt für nicht-kommerzielle Nutzung |
Ohne OPENSANCTIONS_API_KEY läuft der Tracker normal – Sanktionsabzeichen werden einfach in den Karten-Popups weggelassen.
Alle einstellbaren Werte befinden sich oben in shadow_tracker.py:
Umgebungsvariablen:
| Variable | Erforderlich | Beschreibung |
|---|---|---|
AISSTREAM_API_KEY | Ja | AISStream-WebSocket-API-Schlüssel |
OPENSANCTIONS_API_KEY | Nein | Aktiviert Sanktionssuche in Karten-Popups |
OpenSanctions ist für nicht-kommerzielle Nutzung kostenlos – registrieren Sie sich unter opensanctions.org/api. Ohne Schlüssel läuft der Tracker normal; Sanktionsfelder werden in Popups weggelassen.
war-sanctions.gur.gov.ua ist der öffentliche Katalog des ukrainischen GUR für Schattenflotten- und sanktionierte Schiffe. Jedes Schiff hat eine nummerierte Seite mit Hafenanrufverlauf, Karten und Sanktionsdetails.
gur_scrape.py erstellt ein lokales IMO→GUR-ID-Mapping, indem es den Katalog einmal durchsucht:
python gur_scrape.py
Dies erzeugt gur_mapping.json. Der Tracker lädt dies beim Start und verwendet direkte Deep-Links (/en/transport/ships/{id}) in Karten-Popups, wo verfügbar, andernfalls eine Google-Suche auf der Website für Schiffe, die noch nicht im Katalog sind.
Der Durchlauf deckt ~1600 Einträge bei 1,5 s/Anfrage ab (~40 Min.). Führen Sie ihn gelegentlich erneut aus, während der Katalog wächst – verwenden Sie --start, um ab einer bestimmten ID fortzusetzen:
python gur_scrape.py --start 1580
Zum Debuggen einer einzelnen Seite:
python gur_scrape.py --probe 1517
gur_mapping.json wird automatisch alle 5 Minuten zusammen mit der Beobachtungsliste neu geladen – kein Tracker-Neustart nach einem erneuten Durchlauf erforderlich.
Trödelschwellwerte in loitering_module.py:
| Konstante | Standard | Beschreibung |
|---|---|---|
SPEED_THRESHOLD_KN |
Umschlagseinstellungen in transshipment_module.py:
| Konstante | Standard | Beschreibung |
|---|---|---|
Abgedeckte Hafenflächen:
| Hafen | Typ |
|---|---|
| Ust-Luga, Primorsk, St. Petersburg, Wyborg | Russische Exportterminals |
| Skaw/Skagen, Göteborg, Kiel, Kopenhagen, Aarhus | Westliche Umschlagsknotenpunkte |
Jede Schiffsseite (/vessel/<mmsi>) zeigt:
Markiert zwei aus Positionsdaten abgeleitete Muster:
Hafenanrufe werden aus Positions-Pings abgeleitet – keine externe API erforderlich. Ein Schiff muss sich mit <1,5 kn für mindestens 2 Pings innerhalb einer Hafenfläche befinden, um als Anruf registriert zu werden. Dies zeigt ein Muster, kein Urteil.
filtered_cables.kml stammt aus Open-Source-Datensätzen und ist ungefähr – nicht für präzise Näherungsberechnungen geeignet. Der 10-km-Alarmschwellwert berücksichtigt diese Toleranz. Beiträge mit verbesserter Kabelgeometrie sind willkommen.
Der Tracker führt eine einzelne asynchrone Schleife über den WebSocket-Stream aus. Der Zustand wird im Speicher gehalten (vessel_info, static_cache) und bei jedem Renderzyklus in SQLite geschrieben. Die Karte wird atomar über os.replace geschrieben, um die Auslieferung einer partiellen Datei zu vermeiden.
Beim Start liest warm_restart() die letzten WARM_RESTART_HOURS Stunden aus vessel_data_log.db und befüllt vessel_info vor, bevor der WebSocket verbindet. Die erste Karte rendert sofort. Veraltete Positionen werden visuell unterschieden (graue Markierungen, abgedunkelte Spuren) und wechseln zu Live-Farben, sobald neue Pings eintreffen.
Beobachtungsliste und gur_mapping.json werden alle 5 Minuten von der Festplatte neu geladen – Schiffe können hinzugefügt und das GUR-Mapping erneut durchsucht werden, während der Tracker läuft, ohne Neustart.
Der Webserver ist zustandslos und liest direkt aus den SQLite-Dateien – kein gemeinsamer Speicher mit dem Tracker-Prozess.
| Schiff | IMO | Ereignis |
|---|
| EAGLE S | 9329760 | Von Finnland beschlagnahmt Dez. 2024 – Sabotage des Estlink-2-Kabels, Spionageausrüstung gefunden |
| EVENTIN | 9308065 | Von Deutschland beschlagnahmt Jan.–Mär. 2025 – vor Rügen abgetrieben, 100.000 t russisches Rohöl konfisziert |
| KIWALA | 9332810 | Von Estland festgesetzt Apr. 2025 – flaggenlos, EU/UK sanktioniert |
| JAGUAR | 9293002 | Von Estland abgefangen Mai 2025 – Russland schickte Su-35 zur Eskorte |
| KIRA K | 9346720 | Wagner/GRU-Besatzung im Dez. 2025 bestätigt |
| QENDIL | 9310525 | Wagner/GRU-Besatzung Sep. 2025 – ukrainischer Drohnenangriff Dez. 2025 |
| FITBURG | 9250397 | Von Finnland beschlagnahmt 31. Dez. 2025 – Sabotage des Helsinki-Tallinn-Kabels |
| CAFFA | 9143611 | Von Schweden beschlagnahmt 6. Mär. 2026 – gestohlenes ukrainisches Getreide, falsche Flagge |
| SEA OWL I | 9321172 | Von Schweden beschlagnahmt 12. Mär. 2026 – EU-sanktioniert, falsche Flagge der Komoren |
| aisstream.io |
| OpenSanctions | Nein | Ja – nicht-kommerzielle Nutzung | opensanctions.org/api |
| Konstante | Standard | Beschreibung |
|---|
BALTIC_BBOX | 52,65–66°N, 9–30°E | Begrenzungsrahmen für AISStream-Abonnement |
CABLE_ALERT_KM | 10 | Schwellwert für Kabelnähe |
MAP_RENDER_INTERVAL | 180 s | Takt der Kartenaktualisierung |
MMSI_RELOAD_INTERVAL | 300 s | Neuladen der Beobachtungsliste aus der DB |
WARM_RESTART_HOURS | 24 | Stunden des Positionsverlaufs, die beim Start geladen werden |
RECONNECT_DELAY | 10 s | WS-Wiederverbindungs-Backoff |
| 0,5 kn |
| Darunter gilt als gestoppt |
TIME_THRESHOLD_S | 1200 s | Dauer, bevor Trödeln gemeldet wird |
RADIUS_THRESHOLD_KM | 0,5 km | Max. Drift, um noch als gleicher Ort zu gelten |
SPEED_THRESHOLD_KN| 1,5 kn |
| Darunter innerhalb einer Hafenfläche gilt als Anruf |
MIN_PINGS_IN_ZONE | 2 | Mindestanzahl Pings vor Aufzeichnung eines Hafenanrufs |
WINDOW_DAYS | 21 | Max. Tage zwischen Hafenanrufen, um als Umschlag zu gelten |
| Route | Beschreibung |
|---|
/ | Live-Protokollansicht, aktualisiert sich alle 60 s selbst |
/map | Neueste gerenderte Karte (iframe-kompatibel) |
/analyze | Filterbare Schiffstabelle mit CSV-Export |
/timeline | Aktivitätsübersicht – eine Karte pro verfolgtem Schiff, sortiert nach letztem Sichten |
/vessel/<mmsi> | Schiffsverlauf: AIS-Ausfälle, Kabelnähe, Flaggenwechsel, GPX-Export |
/vessel/<mmsi>/gpx | GPX-Track-Export – öffnet in QGIS, OpenStreetMap, GPSBabel |
/loitering | Trödelereignisse mit Kabelnähe-Markierung, Zusammenfassung pro Schiff, CSV-Export |
/transshipment | Russland↔Westen-Hafenanrufmuster – RU→WEST- und WEST→RU-Ereignisse, CSV-Export |
/log/download | Roh-Protokolldatei-Download |
| Komponente | Zustand |
|---|
| AIS-Stream-Consumer | Funktioniert |
| Beobachtungsliste | Über 1.200 Schiffe (vollständiger GUR-Katalog) |
| Kabelnähe-Alarm | Funktioniert |
| Trödelerkennung | Funktioniert |
| Umschlagserkennung | Funktioniert – Hafenanrufe aus Position abgeleitet, 21-Tage-Fenster |
| Karten-Rendering | Funktioniert – 3-Minuten-Takt, Live-Countdown, automatisches Neuladen |
| Warmneustart | Funktioniert – vorbelegt Karte aus letzten 24h Protokoll beim Start |
| Schiffs-Popups | Direkte Links zu MarineTraffic + VesselFinder + War&Sanctions (1.337 Schiffe abgebildet) |
| Schiffs-Popups | OpenSanctions-Sanktionsstatus (optional, API-Schlüssel erforderlich) |
| Flaggenwechselerkennung | Funktioniert – protokolliert Änderungen, gespeichert in flag_history |
| Dashboard – Protokoll, Schiffe, CSV-Export | Funktioniert |
| Dashboard – Zeitstrahlansicht | Funktioniert |
| Dashboard – Schiffsverlauf + GPX | Funktioniert |
| Dashboard – Routenwiedergabe | Funktioniert – Play/Pause/Zurücksetzen, Geschwindigkeitsregler, Hervorhebung von Kabelalarmen |
| Dashboard – Trödel-Panel | Funktioniert |
| Dashboard – Umschlags-Panel | Funktioniert |