
Tracker open source della flotta ombra del Mar Baltico. 1200+ navi, AIS in tempo reale, avvisi di prossimità ai cavi. Nessun cloud, nessun abbonamento, funziona localmente.
Monitoraggio della flotta ombra del Mar Baltico tramite dati AIS in tempo reale. Osserva oltre 1200 navi dal catalogo War&Sanctions del GUR ucraino confrontandole con il feed WebSocket di AISStream, traccia le posizioni su una mappa auto-aggiornante, segnala la prossimità ai cavi sottomarini e rileva schemi di trasbordo Russia↔Occidente.
Gratuito, open source, funziona localmente. Nessun cloud, nessun abbonamento oltre a una chiave API AISStream gratuita.
Parte della catena di strumenti di intelligence sovrana di Former Lab.
Shadow Fleet Tracker Light è costruito e mantenuto dal team di Former Lab — computing sovrano, costruzione di strumenti aperti e privacy-first su vecchio hardware senza supporto di venture capital.
Se questo strumento ti è utile, considera di supportarci su Patreon. È disponibile un livello gratuito, con una prova di 7 giorni sui livelli a pagamento. Sostieni sempre l'Ucraina!
Blog post sul tracker, screenshot e altro: https://www.patreon.com/posts/153285871
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 # free at aisstream.io
python shadow_tracker.py &
uvicorn webserver:app --host 0.0.0.0 --port 8000
Apri http://localhost:8000 — la mappa è su /map.
Utenti Windows: vedi la sezione Setup per la sintassi delle variabili d'ambiente in PowerShell/cmd.
Gli utenti Linux / macOS possono usare start.sh. Gli utenti Windows possono usare start.bat o start.ps1 — entrambi aprono tracker e dashboard in finestre separate e avviano il browser automaticamente.
Vessels1.db)filtered_cables.kml)Mappa di base CartoDB scura con rotte dei cavi sovrapposte in verde. I marcatori delle navi si aggiornano ogni 3 minuti con un conto alla rovescia dal vivo fino al prossimo aggiornamento — la pagina si ricarica automaticamente. I popup mostrano MMSI, IMO, bandiera, velocità, rotta, destinazione e collegamenti diretti a MarineTraffic, VesselFinder e War&Sanctions. Lo stato delle sanzioni OpenSanctions viene mostrato se è configurata una chiave API.
I nomi delle navi noti da Vessels1.db vengono mostrati immediatamente alla prima apparizione senza attendere un messaggio AIS statico.
All'avvio la mappa viene pre-popolata con le ultime 24 ore del registro delle posizioni. Le navi non ancora viste nella sessione corrente vengono mostrate come marcatori grigi con un'etichetta "vista l'ultima volta Xh fa" — passano al colore vivo quando arrivano nuovi ping.
shadow_tracker.py Processo principale — consumatore WS, renderer mappa, scritture DB
loitering_module.py Rilevamento stazionamento e annotazione mappa
transshipment_module.py Rilevamento scali portuali e segnalazione trasbordo Russia↔Occidente
webserver.py Dashboard FastAPI
gur_scrape.py Scraper one-shot — costruisce mappatura IMO→GUR-ID + catalogo completo navi
check.py Controlli preflight — versione Python, dipendenze, chiave API, rete
start.sh Avviatore per Linux / macOS
start.bat Avviatore per Windows (Prompt dei comandi)
start.ps1 Avviatore per Windows (PowerShell)
requirements.txt Dipendenze
Vessels1.db Watchlist — oltre 1200 navi (MMSI + IMO + nome dove noto)
vessel_data_log.db Registro posizioni — runtime, solo append
vessel_static.db Cache dati statici AIS (nome, destinazione, storico bandiere)
transshipment.db Registro scali portuali ed eventi di trasbordo
loitering_events.db Eventi di stazionamento
filtered_cables.kml Geometria cavi sottomarini Mar Baltico
cables.kml Dataset completo cavi (sorgente pre-filtro)
gur_mapping.json Mappatura IMO→GUR-ID (prodotta da gur_scrape.py, opzionale)
gur_vessels_full.json Catalogo GUR completo — GUR-ID→{imo, mmsi, name, flag} (prodotto da gur_scrape.py)
Vessels1.db è l'elenco canonico delle navi tracciate. Oltre 1200 voci provenienti dal catalogo War&Sanctions del GUR ucraino, che copre petroliere e navi da carico identificate come parte della flotta ombra russa o coinvolte in attività ibride nel Baltico.
L'elenco include navi recentemente sequestrate o intercettate dalle autorità baltiche:
Gli MMSI sono corretti rispetto ai dati AIS correnti — le navi della flotta ombra cambiano frequentemente bandiera. Il file update_vessels_2026_03.sql documenta tutte le modifiche con le fonti.
La watchlist è intenzionalmente aperta. Aggiungi navi direttamente a Vessels1.db mentre il tracker è in esecuzione — verranno rilevate entro 5 minuti senza riavvio.
Linux / macOS:
sqlite3 Vessels1.db "INSERT OR IGNORE INTO vessels (mmsi, imo, name) VALUES ('123456789', '9999999', 'VESSEL NAME');"
Windows (nessuna CLI sqlite3 necessaria):
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 — watchlist
vessels(mmsi TEXT PK, imo TEXT, name TEXT, destination TEXT)
vessel_data_log.db — registro posizioni
vessel_data_log(timestamp, mmsi, name, imo, destination, speed, heading, latitude, longitude, cable_alert)
Solo append. Scritto a ogni ciclo di rendering della mappa.
vessel_static.db — cache dati statici AIS
vessel_static(mmsi TEXT PK, name TEXT, imo TEXT, destination TEXT, flag TEXT)
flag_history(id, mmsi, flag, timestamp)
Popolato dai messaggi AIS ShipStaticData in tempo reale. Ha priorità sui nomi di Vessels1.db una volta ricevuti. flag_history registra ogni cambio di bandiera con timestamp — gli eventi di cambio bandiera vengono registrati e visualizzati sulla pagina per nave.
loitering_events.db — registro stazionamento (creato a runtime)
loitering_events(id, mmsi, timestamp, latitude, longitude, near_cable)
transshipment.db — registro scali e trasbordi (creato a runtime)
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.txtIl modo più semplice per eseguire il tracker. Ogni script installa le dipendenze, esegue controlli preliminari e avvia entrambi i processi.
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 (Command Prompt):
set AISSTREAM_API_KEY=your_key_here
start.bat
Tutti e tre gli script eseguono prima check.py — un controllo preliminare che verifica la versione di Python, le dipendenze, i file di dati, la chiave API e la raggiungibilità della rete, con messaggi di errore chiari se manca qualcosa.
Puoi anche eseguire il controllo preliminare da solo in qualsiasi momento:
python check.py
1. Clona e installa le dipendenze
git clone https://github.com/FormerLab/shadow-fleet-tracker-light.git
cd shadow-fleet-tracker-light
pip install -r requirements.txt
Su Ubuntu 22.04+ e Debian 12+ potresti ricevere un errore "externally managed environment" da pip. Gli script di avvio gestiscono automaticamente questo problema creando un ambiente virtuale — basta eseguire start.sh e ci pensa lui. Se installi manualmente, crea prima un venv:
python3 -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
pip install -r requirements.txt
2. Imposta la tua chiave API AISStream
Linux / macOS:
export AISSTREAM_API_KEY=your_key_here
export OPENSANCTIONS_API_KEY=your_key_here # optional
Windows (Command Prompt):
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. Esegui tracker e dashboard in due terminali separati
Terminale 1 — il consumatore del feed AIS:
python shadow_tracker.py
Terminale 2 — il web dashboard:
uvicorn webserver:app --host 0.0.0.0 --port 8000
4. Apri il dashboard
http://localhost:8000
La mappa live è su http://localhost:8000/map e si aggiorna ogni 3 minuti. Al primo avvio la mappa viene renderizzata immediatamente da qualsiasi cronologia delle posizioni già presente nel database — nessuna attesa su schermo vuoto.
| Servizio | Richiesto | Livello gratuito | Collegamento |
|---|---|---|---|
| AISStream | Sì | Sì — illimitato per uso non commerciale |
Senza OPENSANCTIONS_API_KEY il tracker funziona normalmente — i badge delle sanzioni vengono semplicemente omessi dai popup della mappa.
Tutti i parametri regolabili sono all'inizio di shadow_tracker.py:
Variabili d'ambiente:
| Variabile | Richiesta | Descrizione |
|---|---|---|
AISSTREAM_API_KEY | Sì | Chiave API WebSocket AISStream |
OPENSANCTIONS_API_KEY | No | Abilita ricerca sanzioni nei popup della mappa |
OpenSanctions è gratuito per uso non commerciale — registrati su opensanctions.org/api. Senza chiave il tracker funziona normalmente; i campi delle sanzioni vengono omessi dai popup.
war-sanctions.gur.gov.ua è il catalogo pubblico del GUR ucraino delle navi della flotta ombra e sanzionate. Ogni nave ha una pagina numerata con cronologia degli scali, mappe e dettagli sulle sanzioni.
gur_scrape.py costruisce una mappatura locale IMO→GUR-ID eseguendo il crawl del catalogo una volta:
python gur_scrape.py
Questo produce gur_mapping.json. Il tracker lo carica all'avvio e utilizza collegamenti diretti (/en/transport/ships/{id}) nei popup della mappa dove disponibili, ricadendo su una ricerca Google site per le navi non ancora nel catalogo.
Il crawl copre circa 1600 voci a 1,5 s/richiesta (~40 min). Eseguilo di nuovo occasionalmente man mano che il catalogo cresce — usa --start per riprendere da un ID specifico:
python gur_scrape.py --start 1580
Per eseguire il debug di una singola pagina:
python gur_scrape.py --probe 1517
gur_mapping.json viene ricaricato automaticamente ogni 5 minuti insieme alla watchlist — nessun riavvio del tracker necessario dopo un re-crawl.
Soglie di stazionamento in loitering_module.py:
| Costante | Predefinito | Descrizione |
|---|---|---|
SPEED_THRESHOLD_KN |
Impostazioni di trasbordo in transshipment_module.py:
| Costante | Predefinito | Descrizione |
|---|---|---|
Zone portuali coperte:
| Porto | Tipo |
|---|---|
| Ust-Luga, Primorsk, San Pietroburgo, Vyborg | Terminali di esportazione russi |
| Skaw/Skagen, Gothenburg, Kiel, Copenhagen, Aarhus | Hub di trasbordo occidentali |
Ogni pagina nave (/vessel/<mmsi>) mostra:
Segnala due schemi dedotti dai dati di posizione:
Gli scali portuali sono dedotti dai ping di posizione — nessuna API esterna richiesta. Una nave deve essere all'interno di una zona portuale a <1,5 kn per almeno 2 ping per registrare uno scalo. Questo mostra uno schema, non un verdetto.
filtered_cables.kml deriva da dataset open-source ed è approssimativo — non adatto per calcoli precisi di prossimità. La soglia di allarme di 10 km tiene conto di questo margine. Contributi per una geometria dei cavi migliorata sono benvenuti.
Il tracker esegue un singolo loop asincrono sul flusso WebSocket. Lo stato è mantenuto in memoria (vessel_info, static_cache) e riversato in SQLite a ogni ciclo di rendering. La mappa viene scritta atomicamente tramite os.replace per evitare di servire un file parziale.
All'avvio, warm_restart() legge le ultime WARM_RESTART_HOURS ore da vessel_data_log.db e pre-popola vessel_info prima che il WebSocket si connetta. La mappa iniziale viene renderizzata immediatamente. Le posizioni obsolete sono visivamente distinte (marcatori grigi, tracce attenuate) e passano al colore vivo quando arrivano nuovi ping.
Watchlist e gur_mapping.json vengono ricaricati dal disco ogni 5 minuti — le navi possono essere aggiunte e la mappatura GUR ri-crawled mentre il tracker è in esecuzione, senza riavvio.
Il webserver è stateless e legge direttamente dai file SQLite — nessuna memoria condivisa con il processo tracker.
| Nave | IMO | Evento |
|---|
| EAGLE S | 9329760 | Finlandia sequestrata Dic 2024 — sabotaggio cavo Estlink-2, trovata attrezzatura spia |
| EVENTIN | 9308065 | Germania sequestrata Gen-Mar 2025 — alla deriva al largo di Rügen, confiscate 100.000t di greggio russo |
| KIWALA | 9332810 | Estonia fermata Apr 2025 — senza bandiera, sanzionata UE/UK |
| JAGUAR | 9293002 | Estonia intercettata Mag 2025 — Russia ha fatto decollare Su-35 per scortarla |
| KIRA K | 9346720 | Equipaggio Wagner/GRU confermato a bordo Dic 2025 |
| QENDIL | 9310525 | Equipaggio Wagner/GRU Set 2025 — attacco drone ucraino Dic 2025 |
| FITBURG | 9250397 | Finlandia sequestrata 31 Dic 2025 — sabotaggio cavo Helsinki-Tallinn |
| CAFFA | 9143611 | Svezia sequestrata 6 Mar 2026 — grano ucraino rubato, bandiera falsa |
| SEA OWL I | 9321172 | Svezia sequestrata 12 Mar 2026 — sanzionata UE, bandiera Comore falsa |
| OpenSanctions | No | Sì — uso non commerciale | opensanctions.org/api |
| Costante | Predefinito | Descrizione |
|---|
BALTIC_BBOX | 52.65–66°N, 9–30°E | Bounding box di sottoscrizione AISStream |
CABLE_ALERT_KM | 10 | Soglia di prossimità cavi |
MAP_RENDER_INTERVAL | 180 s | Cadenza aggiornamento mappa |
MMSI_RELOAD_INTERVAL | 300 s | Ricarica watchlist dal DB |
WARM_RESTART_HOURS | 24 | Ore di cronologia posizioni da caricare all'avvio |
RECONNECT_DELAY | 10 s | Backoff riconnessione WS |
| 0.5 kn |
| Sotto questa conta come fermo |
TIME_THRESHOLD_S | 1200 s | Durata prima che lo stazionamento venga segnalato |
RADIUS_THRESHOLD_KM | 0.5 km | Deriva massima per contare ancora come stesso punto |
SPEED_THRESHOLD_KN| 1.5 kn |
| Sotto questa all'interno di una zona portuale conta come scalo |
MIN_PINGS_IN_ZONE | 2 | Numero minimo di ping prima di registrare uno scalo |
WINDOW_DAYS | 21 | Giorni massimi tra scali per segnalare come trasbordo |
| Rotta | Descrizione |
|---|
/ | Visualizzatore log in tempo reale, auto-aggiornamento ogni 60 s |
/map | Ultima mappa renderizzata (compatibile iframe) |
/analyze | Tabella record nave filtrabile con esportazione CSV |
/timeline | Panoramica attività — una scheda per nave tracciata, ordinata per ultima visualizzazione |
/vessel/<mmsi> | Cronologia per nave: blackout AIS, prossimità cavi, cambi bandiera, esportazione GPX |
/vessel/<mmsi>/gpx | Esportazione traccia GPX — si apre in QGIS, OpenStreetMap, GPSBabel |
/loitering | Eventi di stazionamento con flag vicino cavo, riepilogo per nave, esportazione CSV |
/transshipment | Schemi di scalo Russia↔Occidente — eventi RU→OVEST e OVEST→RU, esportazione CSV |
/log/download | Download file log grezzo |
| Componente | Stato |
|---|
| Consumatore flusso AIS | Funzionante |
| Watchlist | Oltre 1200 navi (catalogo GUR completo) |
| Allarme prossimità cavi | Funzionante |
| Rilevamento stazionamento | Funzionante |
| Rilevamento trasbordo | Funzionante — scali dedotti dalla posizione, finestra di 21 giorni |
| Rendering mappa | Funzionante — cadenza 3 min, conto alla rovescia live, auto-ricarica |
| Riavvio a caldo | Funzionante — pre-popola mappa dalle ultime 24h di log all'avvio |
| Popup nave | Collegamenti diretti MarineTraffic + VesselFinder + War&Sanctions (1337 navi mappate) |
| Popup nave | Stato sanzioni OpenSanctions (opzionale, richiede chiave API) |
| Rilevamento cambio bandiera | Funzionante — registra cambi, memorizzato in flag_history |
| Dashboard — log, navi, esportazione CSV | Funzionante |
| Dashboard — vista timeline | Funzionante |
| Dashboard — cronologia per nave + GPX | Funzionante |
| Dashboard — riproduzione percorso | Funzionante — play/pausa/ripristino, controllo velocità, evidenziazione allarme cavo |
| Dashboard — pannello stazionamento | Funzionante |
| Dashboard — pannello trasbordo | Funzionante |