
Outil open source de suivi de la flotte fantôme de la mer Baltique. 1200+ navires, AIS en direct, alertes de proximité de câbles. Pas de cloud, pas d'abonnement, fonctionne localement
Surveillance de la flotte fantôme en mer Baltique via les données AIS en direct. Surveille plus de 1200 navires du catalogue GUR War&Sanctions ukrainien par rapport au flux WebSocket AISStream, affiche les positions sur une carte auto-mise à jour, signale la proximité des câbles sous-marins et détecte les schémas de transbordement Russie↔Occident.
Gratuit, open source, fonctionne localement. Pas de cloud, pas d'abonnement au-delà d'une clé API AISStream gratuite.
Fait partie de la chaîne d'outils souverains du Former Lab.
Shadow Fleet Tracker Light est construit et maintenu par l'équipe de Former Lab — informatique souveraine, construisant des outils open source et respectueux de la vie privée sur du vieux matériel, sans financement VC.
Si cet outil vous est utile, envisagez de nous soutenir sur Patreon. Un niveau gratuit est disponible, avec un essai de 7 jours sur les niveaux payants. Soutenez toujours l'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 # gratuit sur aisstream.io
python shadow_tracker.py &
uvicorn webserver:app --host 0.0.0.0 --port 8000
Ouvrez http://localhost:8000 — la carte est à /map.
Utilisateurs Windows : voir la section Configuration pour la syntaxe des variables d'environnement dans PowerShell/cmd.
Les utilisateurs Linux / macOS peuvent utiliser start.sh. Les utilisateurs Windows peuvent utiliser start.bat ou start.ps1 — les deux ouvrent le tracker et le tableau de bord dans des fenêtres séparées et lancent le navigateur automatiquement.
Vessels1.db)filtered_cables.kml)Fond de carte CartoDB sombre avec les itinéraires de câbles superposés en vert. Les marqueurs de navires se mettent à jour toutes les 3 minutes avec un compte à rebours en direct jusqu'à la prochaine actualisation — la page se recharge automatiquement. Les popups montrent MMSI, IMO, pavillon, vitesse, cap, destination et des liens profonds vers MarineTraffic, VesselFinder et War&Sanctions. Le statut des sanctions OpenSanctions est affiché si une clé API est configurée.
Les noms de navires connus de Vessels1.db sont affichés immédiatement à leur première apparition sans attendre un message statique AIS.
Au démarrage, la carte est préremplie avec les dernières 24 heures du journal de position. Les navires non encore vus dans la session en cours sont affichés avec des marqueurs gris et une étiquette "vu pour la dernière fois il y a Xh" — ils passent à la couleur en direct à mesure que de nouveaux pings arrivent.
shadow_tracker.py Processus principal — consommateur WS, rendu de carte, écritures DB
loitering_module.py Détection de flânerie et annotation de carte
transshipment_module.py Détection d'escales portuaires et signalement de transbordement Russie↔Occident
webserver.py Tableau de bord FastAPI
gur_scrape.py Récupérateur unique — construit le mapping IMO→GUR-ID + catalogue complet des navires
check.py Vérifications préalables — version Python, dépendances, clé API, réseau
start.sh Lanceur pour Linux / macOS
start.bat Lanceur pour Windows (Invité de commandes)
start.ps1 Lanceur pour Windows (PowerShell)
requirements.txt Dépendances
Vessels1.db Liste de surveillance — plus de 1200 navires (MMSI + IMO + nom si connu)
vessel_data_log.db Journal de position — exécution, ajout seulement
vessel_static.db Cache des données statiques AIS (nom, destination, historique des pavillons)
transshipment.db Journal des escales portuaires et événements de transbordement
loitering_events.db Événements de flânerie
filtered_cables.kml Géométrie des câbles sous-marins de la mer Baltique
cables.kml Jeu de données complet des câbles (source pré-filtrage)
gur_mapping.json Mapping IMO→GUR-ID (produit par gur_scrape.py, optionnel)
gur_vessels_full.json Catalogue GUR complet — GUR-ID→{imo, mmsi, name, flag} (produit par gur_scrape.py)
Vessels1.db est la liste canonique des navires suivis. Plus de 1200 entrées provenant du catalogue GUR War&Sanctions ukrainien, couvrant les pétroliers et cargos identifiés comme faisant partie de la flotte fantôme russe ou impliqués dans des activités hybrides en Baltique.
La liste comprend des navires récemment saisis ou interceptés par les autorités baltes :
Les MMSI sont corrigés par rapport aux données AIS actuelles — les navires de la flotte fantôme changent fréquemment de pavillon. Le fichier update_vessels_2026_03.sql documente tous les changements avec leurs sources.
La liste de surveillance est intentionnellement ouverte. Ajoutez des navires directement dans Vessels1.db pendant que le tracker fonctionne — ils seront pris en compte dans les 5 minutes sans redémarrage.
Linux / macOS :
sqlite3 Vessels1.db "INSERT OR IGNORE INTO vessels (mmsi, imo, name) VALUES ('123456789', '9999999', 'VESSEL NAME');"
Windows (pas besoin d'interface CLI sqlite3) :
import sqlite3
conn = sqlite3.connect("Vessels1.db")
conn.execute("INSERT OR IGNORE INTO vessels (mmsi, imo, name) VALUES ('123456789', '9999999', 'VESSEL NAME')")
conn.commit()
conn.close()
Vessels1.db — liste de surveillance
vessels(mmsi TEXT PK, imo TEXT, name TEXT, destination TEXT)
vessel_data_log.db — journal de position
vessel_data_log(timestamp, mmsi, name, imo, destination, speed, heading, latitude, longitude, cable_alert)
Ajout seulement. Écrit à chaque cycle de rendu de la carte.
vessel_static.db — cache des données statiques AIS
vessel_static(mmsi TEXT PK, name TEXT, imo TEXT, destination TEXT, flag TEXT)
flag_history(id, mmsi, flag, timestamp)
Peuplé à partir des messages AIS ShipStaticData en direct. Prend la priorité sur les noms de Vessels1.db une fois reçus. flag_history enregistre chaque changement de pavillon avec un horodatage — les événements de changement de pavillon sont enregistrés et affichés sur la page par navire.
loitering_events.db — journal des flâneries (créé à l'exécution)
loitering_events(id, mmsi, timestamp, latitude, longitude, near_cable)
transshipment.db — journal des escales portuaires et transbordements (créé à l'exécution)
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.txtLa façon la plus simple d'exécuter le tracker. Chaque script installe les dépendances, exécute les vérifications préalables et lance les deux processus.
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 (Invité de commandes) :
set AISSTREAM_API_KEY=your_key_here
start.bat
Les trois scripts exécutent d'abord check.py — une vérification préalable qui vérifie la version Python, les dépendances, les fichiers de données, la clé API et la connectivité réseau, avec des messages d'erreur clairs si quelque chose manque.
Vous pouvez également exécuter la vérification préalable seule à tout moment :
python check.py
1. Cloner et installer les dépendances
git clone https://github.com/FormerLab/shadow-fleet-tracker-light.git
cd shadow-fleet-tracker-light
pip install -r requirements.txt
Sur Ubuntu 22.04+ et Debian 12+, vous pouvez obtenir une erreur "externally managed environment" de pip. Les scripts de démarrage gèrent cela automatiquement en créant un environnement virtuel — exécutez simplement start.sh et il s'en occupe. Si vous installez manuellement, créez d'abord un venv :
python3 -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
pip install -r requirements.txt
2. Définir votre clé API AISStream
Linux / macOS :
export AISSTREAM_API_KEY=your_key_here
export OPENSANCTIONS_API_KEY=your_key_here # optionnel
Windows (Invité de commandes) :
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. Exécuter le tracker et le tableau de bord dans deux terminaux séparés
Terminal 1 — le consommateur du flux AIS :
python shadow_tracker.py
Terminal 2 — le tableau de bord web :
uvicorn webserver:app --host 0.0.0.0 --port 8000
4. Ouvrir le tableau de bord
http://localhost:8000
La carte en direct est à http://localhost:8000/map et se met à jour toutes les 3 minutes. Lors du premier lancement, la carte s'affiche immédiatement à partir de tout historique de position déjà présent dans la base de données — pas d'attente à blanc.
| Service | Requis | Niveau gratuit | Lien |
|---|---|---|---|
| AISStream | Oui | Oui — illimité pour usage non commercial | aisstream.io |
Sans OPENSANCTIONS_API_KEY, le tracker fonctionne normalement — les badges de sanctions sont simplement omis des popups de la carte.
Tous les paramètres ajustables se trouvent en haut de shadow_tracker.py :
Variables d'environnement :
| Variable | Requise | Description |
|---|---|---|
AISSTREAM_API_KEY | Oui | Clé API WebSocket AISStream |
OPENSANCTIONS_API_KEY | Non | Active la recherche de sanctions dans les popups de la carte |
OpenSanctions est gratuit pour un usage non commercial — inscrivez-vous sur opensanctions.org/api. Sans clé, le tracker fonctionne normalement ; les champs de sanctions sont omis des popups.
war-sanctions.gur.gov.ua est le catalogue public du GUR ukrainien des navires de la flotte fantôme et sanctionnés. Chaque navire a une page numérotée avec l'historique des escales, des cartes et des détails sur les sanctions.
gur_scrape.py construit un mapping local IMO→GUR-ID en parcourant le catalogue une fois :
python gur_scrape.py
Cela produit gur_mapping.json. Le tracker le charge au démarrage et utilise des liens profonds directs (/en/transport/ships/{id}) dans les popups de la carte lorsque disponibles, avec un recours à une recherche site Google pour les navires pas encore dans le catalogue.
Le parcours couvre environ 1600 entrées à 1,5 s/requête (~40 min). Réexécutez-le occasionnellement à mesure que le catalogue s'agrandit — utilisez --start pour reprendre à partir d'un ID spécifique :
python gur_scrape.py --start 1580
Pour déboguer une seule page :
python gur_scrape.py --probe 1517
gur_mapping.json est rechargé automatiquement toutes les 5 minutes en même temps que la liste de surveillance — pas besoin de redémarrer le tracker après un nouveau parcours.
Seuils de flânerie dans loitering_module.py :
| Constante | Défaut | Description |
|---|---|---|
SPEED_THRESHOLD_KN |
Paramètres de transbordement dans transshipment_module.py :
| Constante | Défaut | Description |
|---|---|---|
Zones portuaires couvertes :
| Port | Type |
|---|---|
| Ust-Luga, Primorsk, Saint-Pétersbourg, Vyborg | Terminaux d'exportation russes |
| Skaw/Skagen, Göteborg, Kiel, Copenhague, Aarhus | Hubs de transbordement occidentaux |
Chaque page de navire (/vessel/<mmsi>) affiche :
Signale deux schémas déduits des données de position :
Les escales portuaires sont déduites des pings de position — aucune API externe requise. Un navire doit être à l'intérieur d'une zone portuaire à <1,5 nd pendant au moins 2 pings pour enregistrer une escale. Cela montre un schéma, pas un verdict.
filtered_cables.kml est dérivé de jeux de données open source et est approximatif — non adapté pour des calculs précis de proximité. Le seuil d'alerte de 10 km tient compte de cette marge. Les contributions d'une meilleure géométrie des câbles sont les bienvenues.
Le tracker exécute une seule boucle asynchrone sur le flux WebSocket. L'état est conservé en mémoire (vessel_info, static_cache) et vidé dans SQLite à chaque cycle de rendu. La carte est écrite atomiquement via os.replace pour éviter de servir un fichier partiel.
Au démarrage, warm_restart() lit les dernières WARM_RESTART_HOURS heures depuis vessel_data_log.db et préremplit vessel_info avant que le WebSocket ne se connecte. La carte initiale s'affiche immédiatement. Les positions obsolètes sont distinguées visuellement (marqueurs gris, traces atténuées) et passent à la couleur en direct à mesure que de nouveaux pings arrivent.
La liste de surveillance et gur_mapping.json sont rechargés depuis le disque toutes les 5 minutes — les navires peuvent être ajoutés et le mapping GUR re-parcouru pendant que le tracker fonctionne, sans redémarrage.
Le serveur web est sans état et lit directement depuis les fichiers SQLite — pas de mémoire partagée avec le processus du tracker.
| Navire | IMO | Événement |
|---|
| EAGLE S | 9329760 | Finlande a saisi déc. 2024 — sabotage du câble Estlink-2, équipement d'espionnage trouvé |
| EVENTIN | 9308065 | Allemagne a saisi janv.–mars 2025 — dérive près de Rügen, 100 000 t de pétrole brut russe confisquées |
| KIWALA | 9332810 | Estonie a détenu avr. 2025 — sans pavillon, sanctionné UE/RU |
| JAGUAR | 9293002 | Estonie a intercepté mai 2025 — la Russie a envoyé un Su-35 pour l'escorter |
| KIRA K | 9346720 | Équipage Wagner/GRU confirmé à bord déc. 2025 |
| QENDIL | 9310525 | Équipage Wagner/GRU sept. 2025 — frappe de drone ukrainien déc. 2025 |
| FITBURG | 9250397 | Finlande a saisi le 31 déc. 2025 — sabotage du câble Helsinki-Tallinn |
| CAFFA | 9143611 | Suède a saisi le 6 mars 2026 — céréales ukrainiennes volées, faux pavillon |
| SEA OWL I | 9321172 | Suède a saisi le 12 mars 2026 — sanctionné UE, faux pavillon des Comores |
| OpenSanctions | Non | Oui — usage non commercial | opensanctions.org/api |
| Constante | Défaut | Description |
|---|
BALTIC_BBOX | 52,65–66°N, 9–30°E | Boîte englobante d'abonnement AISStream |
CABLE_ALERT_KM | 10 | Seuil de proximité de câble |
MAP_RENDER_INTERVAL | 180 s | Cadence d'actualisation de la carte |
MMSI_RELOAD_INTERVAL | 300 s | Rechargement de la liste de surveillance depuis la DB |
WARM_RESTART_HOURS | 24 | Heures d'historique de position à charger au démarrage |
RECONNECT_DELAY | 10 s | Backoff de reconnexion WS |
| 0,5 nd |
| En dessous de ceci, considéré comme arrêté |
TIME_THRESHOLD_S | 1200 s | Durée avant qu'une flânerie soit signalée |
RADIUS_THRESHOLD_KM | 0,5 km | Dérive maximale pour être toujours considéré comme le même point |
SPEED_THRESHOLD_KN| 1,5 nd |
| En dessous de ceci dans une zone portuaire compte comme une escale |
MIN_PINGS_IN_ZONE | 2 | Pings minimum avant d'enregistrer une escale |
WINDOW_DAYS | 21 | Jours maximum entre escales pour signaler un transbordement |
| Route | Description |
|---|
/ | Visualiseur de journal en direct, auto-actualisé toutes les 60 s |
/map | Dernière carte rendue (compatible iframe) |
/analyze | Tableau de fiches navire filtré avec export CSV |
/timeline | Aperçu des activités — une carte par navire suivi, triée par dernier vu |
/vessel/<mmsi> | Historique par navire : pannes AIS, proximité de câble, changements de pavillon, export GPX |
/vessel/<mmsi>/gpx | Export de trace GPX — s'ouvre dans QGIS, OpenStreetMap, GPSBabel |
/loitering | Événements de flânerie avec indicateur près d'un câble, résumé par navire, export CSV |
/transshipment | Schémas d'escales Russie↔Occident — événements RU→OUEST et OUEST→RU, export CSV |
/log/download | Téléchargement du fichier journal brut |
| Composant | État |
|---|
| Consommateur de flux AIS | Fonctionnel |
| Liste de surveillance | Plus de 1200 navires (catalogue GUR complet) |
| Alerte de proximité de câble | Fonctionnel |
| Détection de flânerie | Fonctionnel |
| Détection de transbordement | Fonctionnel — escales déduites de la position, fenêtre de 21 jours |
| Rendu de carte | Fonctionnel — cadence de 3 min, compte à rebours en direct, auto-rechargement |
| Redémarrage à chaud | Fonctionnel — préremplit la carte avec les dernières 24h de journaux au démarrage |
| Popups de navires | Liens directs MarineTraffic + VesselFinder + War&Sanctions (1337 navires mappés) |
| Popups de navires | Statut des sanctions OpenSanctions (optionnel, nécessite clé API) |
| Détection de changement de pavillon | Fonctionnel — enregistre les changements, stocké dans flag_history |
| Tableau de bord — journal, navires, export CSV | Fonctionnel |
| Tableau de bord — vue chronologique | Fonctionnel |
| Tableau de bord — historique par navire + GPX | Fonctionnel |
| Tableau de bord — relecture d'itinéraire | Fonctionnel — lecture/pause/réinitialisation, contrôle de vitesse, mise en évidence des alertes de câble |
| Tableau de bord — panneau de flânerie | Fonctionnel |
| Tableau de bord — panneau de transbordement | Fonctionnel |