
Rastreador de flota fantasma del mar Báltico de código abierto. Más de 1200 buques, AIS en vivo, alertas de proximidad a cables. Sin nube, sin suscripción, se ejecuta localmente.
Monitoreo de la flota fantasma del Mar Báltico mediante datos AIS en vivo. Observa más de 1200 embarcaciones del catálogo GUR de Ucrania sobre Guerra y Sanciones contra el feed WebSocket de AISStream, traza posiciones en un mapa que se actualiza solo, señala la proximidad a cables submarinos y detecta patrones de transbordo Rusia↔Occidente.
Gratuito, de código abierto, se ejecuta localmente. Sin nube, sin suscripción más allá de una clave API gratuita de AISStream.
Parte de la cadena de herramientas de inteligencia soberana de Former Lab.
Shadow Fleet Tracker Light es desarrollado y mantenido por el equipo de Former Lab — computación soberana, construyendo herramientas abiertas y centradas en la privacidad en hardware antiguo sin respaldo de capital de riesgo.
Si esta herramienta te resulta útil, considera apoyar en Patreon. Hay un nivel gratuito disponible, con una prueba de 7 días en los niveles de pago. ¡Siempre apoya a Ucrania!
Entrada de blog sobre el rastreador, capturas de pantalla y más: 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 # gratis en aisstream.io
python shadow_tracker.py &
uvicorn webserver:app --host 0.0.0.0 --port 8000
Abre http://localhost:8000 — el mapa está en /map.
Usuarios de Windows: consulta la sección Configuración para la sintaxis de variables de entorno en PowerShell/cmd.
Usuarios de Linux / macOS pueden usar start.sh. Usuarios de Windows pueden usar start.bat o start.ps1 — ambos abren el rastreador y el panel en ventanas separadas e inician el navegador automáticamente.
Vessels1.db)filtered_cables.kml)Mapa base oscuro de CartoDB con rutas de cables superpuestas en verde. Los marcadores de embarcaciones se actualizan cada 3 minutos con una cuenta regresiva en vivo hasta la siguiente actualización — la página se recarga automáticamente. Las ventanas emergentes muestran MMSI, IMO, bandera, velocidad, rumbo, destino y enlaces profundos a MarineTraffic, VesselFinder y War&Sanctions. El estado de sanciones de OpenSanctions se muestra si se ha configurado una clave API.
Los nombres de embarcaciones conocidos de Vessels1.db se muestran inmediatamente en la primera aparición sin esperar un mensaje estático AIS.
Al inicio, el mapa se puebla previamente con las últimas 24 horas del registro de posiciones. Las embarcaciones no vistas aún en la sesión actual se muestran como marcadores grises con una etiqueta "visto por última vez hace Xh" — pasan a color vivo a medida que llegan nuevos pings.
shadow_tracker.py Proceso principal — consumidor WS, renderizador de mapa, escrituras DB
loitering_module.py Detección de merodeo y anotación en mapa
transshipment_module.py Detección de escalas en puertos y señalización de transbordos Rusia↔Occidente
webserver.py Panel FastAPI
gur_scrape.py Recopilador único — construye mapeo IMO→ID GUR + catálogo completo de embarcaciones
check.py Verificaciones previas — versión de Python, dependencias, clave API, red
start.sh Lanzador para Linux / macOS
start.bat Lanzador para Windows (Símbolo del sistema)
start.ps1 Lanzador para Windows (PowerShell)
requirements.txt Dependencias
Vessels1.db Lista de vigilancia — más de 1200 embarcaciones (MMSI + IMO + nombre cuando se conoce)
vessel_data_log.db Registro de posiciones — solo añadir, en tiempo de ejecución
vessel_static.db Caché de datos estáticos AIS (nombre, destino, historial de bandera)
transshipment.db Registro de escalas en puertos y eventos de transbordo
loitering_events.db Eventos de merodeo
filtered_cables.kml Geometría de cables submarinos del Mar Báltico
cables.kml Conjunto completo de cables (fuente previa al filtro)
gur_mapping.json Mapeo IMO→ID GUR (producido por gur_scrape.py, opcional)
gur_vessels_full.json Catálogo GUR completo — ID GUR→{imo, mmsi, name, flag} (producido por gur_scrape.py)
Vessels1.db es la lista canónica de embarcaciones rastreadas. Más de 1200 entradas obtenidas del catálogo GUR de Ucrania sobre Guerra y Sanciones, que cubre petroleros y buques de carga identificados como parte de la flota fantasma rusa o implicados en actividades híbridas en el Báltico.
La lista incluye embarcaciones recientemente incautadas o interceptadas por autoridades bálticas:
Los MMSI se corrigen con los datos AIS actuales — los buques de la flota fantasma cambian de bandera con frecuencia. El archivo update_vessels_2026_03.sql documenta todos los cambios con fuentes.
La lista de vigilancia está intencionalmente abierta. Añade embarcaciones directamente a Vessels1.db mientras el rastreador está en ejecución — serán recogidas en menos de 5 minutos sin necesidad de reiniciar.
Linux / macOS:
sqlite3 Vessels1.db "INSERT OR IGNORE INTO vessels (mmsi, imo, name) VALUES ('123456789', '9999999', 'VESSEL NAME');"
Windows (sin necesidad de 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 — lista de vigilancia
vessels(mmsi TEXT PK, imo TEXT, name TEXT, destination TEXT)
vessel_data_log.db — registro de posiciones
vessel_data_log(timestamp, mmsi, name, imo, destination, speed, heading, latitude, longitude, cable_alert)
Solo añadir. Se escribe en cada ciclo de renderizado del mapa.
vessel_static.db — caché de datos estáticos AIS
vessel_static(mmsi TEXT PK, name TEXT, imo TEXT, destination TEXT, flag TEXT)
flag_history(id, mmsi, flag, timestamp)
Se puebla a partir de mensajes AIS ShipStaticData en vivo. Tiene prioridad sobre los nombres de Vessels1.db una vez recibidos. flag_history registra cada cambio de bandera con una marca de tiempo — los eventos de cambio de bandera se registran y muestran en la página por embarcación.
loitering_events.db — registro de merodeo (creado en tiempo de ejecución)
loitering_events(id, mmsi, timestamp, latitude, longitude, near_cable)
transshipment.db — registro de escalas en puertos y transbordos (creado en tiempo de ejecución)
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 forma más sencilla de ejecutar el rastreador. Cada script instala dependencias, ejecuta verificaciones previas e inicia ambos procesos.
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 (Símbolo del sistema):
set AISSTREAM_API_KEY=your_key_here
start.bat
Los tres scripts ejecutan check.py primero — una verificación previa que comprueba la versión de Python, dependencias, archivos de datos, clave API y accesibilidad de red, con mensajes de error claros si falta algo.
También puedes ejecutar la verificación previa por separado en cualquier momento:
python check.py
1. Clonar e instalar dependencias
git clone https://github.com/FormerLab/shadow-fleet-tracker-light.git
cd shadow-fleet-tracker-light
pip install -r requirements.txt
En Ubuntu 22.04+ y Debian 12+ es posible que obtengas un error de "entorno administrado externamente" de pip. Los scripts de inicio lo manejan automáticamente creando un entorno virtual — solo ejecuta start.sh y se encarga. Si instalas manualmente, crea un venv primero:
python3 -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
pip install -r requirements.txt
2. Establecer tu clave API de AISStream
Linux / macOS:
export AISSTREAM_API_KEY=your_key_here
export OPENSANCTIONS_API_KEY=your_key_here # opcional
Windows (Símbolo del sistema):
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. Ejecutar el rastreador y el panel en dos terminales separadas
Terminal 1 — el consumidor del feed AIS:
python shadow_tracker.py
Terminal 2 — el panel web:
uvicorn webserver:app --host 0.0.0.0 --port 8000
4. Abrir el panel
http://localhost:8000
El mapa en vivo está en http://localhost:8000/map y se actualiza cada 3 minutos. En la primera ejecución, el mapa se renderiza inmediatamente a partir del historial de posiciones ya existente en la base de datos — sin esperar un lienzo en blanco.
| Servicio | Requerido | Nivel gratuito | Enlace |
|---|---|---|---|
| AISStream | Sí | Sí — ilimitado para uso no comercial | aisstream.io |
Sin OPENSANCTIONS_API_KEY el rastreador funciona normalmente — las insignias de sanciones simplemente se omiten en las ventanas emergentes del mapa.
Todos los parámetros ajustables están al inicio de shadow_tracker.py:
Variables de entorno:
| Variable | Requerida | Descripción |
|---|---|---|
AISSTREAM_API_KEY | Sí | Clave API del WebSocket de AISStream |
OPENSANCTIONS_API_KEY | No | Habilita la consulta de sanciones en las ventanas emergentes del mapa |
OpenSanctions es gratuito para uso no comercial — regístrate en opensanctions.org/api. Sin clave, el rastreador funciona normalmente; los campos de sanciones se omiten en las ventanas emergentes.
war-sanctions.gur.gov.ua es el catálogo público del GUR de Ucrania sobre la flota fantasma y embarcaciones sancionadas. Cada embarcación tiene una página numerada con historial de escalas en puertos, mapas y detalles de sanciones.
gur_scrape.py construye un mapeo local IMO→ID GUR recorriendo el catálogo una vez:
python gur_scrape.py
Esto produce gur_mapping.json. El rastreador lo carga al inicio y utiliza enlaces profundos directos (/en/transport/ships/{id}) en las ventanas emergentes del mapa cuando están disponibles, recurriendo a una búsqueda en el sitio de Google para embarcaciones aún no incluidas en el catálogo.
El recorrido cubre ~1600 entradas a 1,5 s/solicitud (~40 min). Vuelve a ejecutarlo ocasionalmente a medida que el catálogo crece — usa --start para reanudar desde un ID específico:
python gur_scrape.py --start 1580
Para depurar una sola página:
python gur_scrape.py --probe 1517
gur_mapping.json se recarga automáticamente cada 5 minutos junto con la lista de vigilancia — no es necesario reiniciar el rastreador después de un nuevo recorrido.
Límites de merodeo en loitering_module.py:
| Constante | Por defecto | Descripción |
|---|---|---|
SPEED_THRESHOLD_KN |
Ajustes de transbordo en transshipment_module.py:
| Constante | Por defecto | Descripción |
|---|---|---|
Zonas portuarias cubiertas:
| Puerto | Tipo |
|---|---|
| Ust-Luga, Primorsk, San Petersburgo, Vyborg | Terminales de exportación rusas |
| Skaw/Skagen, Gotemburgo, Kiel, Copenhague, Aarhus | Centros de transbordo occidentales |
Cada página de embarcación (/vessel/<mmsi>) muestra:
Señala dos patrones inferidos de los datos de posición:
Las escalas en puertos se infieren a partir de pings de posición — no se requiere API externa. Una embarcación debe estar dentro de una zona portuaria a <1,5 nudos durante al menos 2 pings para registrar una escala. Esto muestra un patrón, no un veredicto.
filtered_cables.kml se deriva de conjuntos de datos de código abierto y es aproximado — no es adecuado para cálculos precisos de proximidad. El umbral de alerta de 10 km tiene en cuenta este margen. Se aceptan contribuciones con geometría de cables mejorada.
El rastreador ejecuta un único bucle asíncrono sobre el flujo WebSocket. El estado se mantiene en memoria (vessel_info, static_cache) y se vuelca a SQLite en cada ciclo de renderizado. El mapa se escribe atómicamente mediante os.replace para evitar servir un archivo parcial.
Al inicio, warm_restart() lee las últimas WARM_RESTART_HOURS horas de vessel_data_log.db y pre-puebla vessel_info antes de que el WebSocket se conecte. El mapa inicial se renderiza inmediatamente. Las posiciones antiguas se distinguen visualmente (marcadores grises, rutas atenuadas) y pasan a color vivo a medida que llegan nuevos pings.
La lista de vigilancia y gur_mapping.json se recargan desde el disco cada 5 minutos — se pueden añadir embarcaciones y volver a recorrer el mapeo GUR mientras el rastreador está en ejecución, sin necesidad de reiniciar.
El servidor web es sin estado y lee directamente de los archivos SQLite — no hay memoria compartida con el proceso del rastreador.
| Embarcación | IMO | Evento |
|---|
| EAGLE S | 9329760 | Finlandia incautó dic 2024 — sabotaje del cable Estlink-2, equipo de espionaje encontrado |
| EVENTIN | 9308065 | Alemania incautó ene–mar 2025 — derivó frente a Rügen, 100 000 t de crudo ruso confiscadas |
| KIWALA | 9332810 | Estonia detuvo abr 2025 — sin bandera, sancionada por UE/Reino Unido |
| JAGUAR | 9293002 | Estonia interceptó may 2025 — Rusia desplegó Su-35 para escoltarlo |
| KIRA K | 9346720 | Tripulación de Wagner/GRU confirmada a bordo dic 2025 |
| QENDIL | 9310525 | Tripulación de Wagner/GRU sep 2025 — ataque de dron ucraniano dic 2025 |
| FITBURG | 9250397 | Finlandia incautó 31 dic 2025 — sabotaje del cable Helsinki-Tallin |
| CAFFA | 9143611 | Suecia incautó 6 mar 2026 — grano ucraniano robado, bandera falsa |
| SEA OWL I | 9321172 | Suecia incautó 12 mar 2026 — sancionada por UE, bandera falsa de Comoras |
| OpenSanctions | No | Sí — uso no comercial | opensanctions.org/api |
| Constante | Por defecto | Descripción |
|---|
BALTIC_BBOX | 52.65–66°N, 9–30°E | Cuadro delimitador de suscripción a AISStream |
CABLE_ALERT_KM | 10 | Umbral de proximidad a cable |
MAP_RENDER_INTERVAL | 180 s | Cadencia de actualización del mapa |
MMSI_RELOAD_INTERVAL | 300 s | Recarga de la lista de vigilancia desde BD |
WARM_RESTART_HOURS | 24 | Horas de historial de posiciones a cargar al inicio |
RECONNECT_DELAY | 10 s | Retroceso de reconexión WS |
| 0,5 kn |
| Por debajo de esto se considera detenido |
TIME_THRESHOLD_S | 1200 s | Duración antes de señalar merodeo |
RADIUS_THRESHOLD_KM | 0,5 km | Deriva máxima para considerarlo el mismo punto |
SPEED_THRESHOLD_KN| 1,5 kn |
| Por debajo de esto dentro de una zona portuaria se considera escala |
MIN_PINGS_IN_ZONE | 2 | Pings mínimos antes de registrar una escala |
WINDOW_DAYS | 21 | Días máximos entre escalas para señalar transbordo |
| Ruta | Descripción |
|---|
/ | Visor de registros en vivo, se actualiza automáticamente cada 60 s |
/map | Último mapa renderizado (compatible con iframes) |
/analyze | Tabla de registros de embarcaciones filtrable con exportación CSV |
/timeline | Resumen de actividad — una tarjeta por embarcación rastreada, ordenada por última vez vista |
/vessel/<mmsi> | Historial por embarcación: cortes AIS, proximidad a cables, cambios de bandera, exportación GPX |
/vessel/<mmsi>/gpx | Exportación de ruta GPX — se abre en QGIS, OpenStreetMap, GPSBabel |
/loitering | Eventos de merodeo con indicador de proximidad a cable, resumen por embarcación, exportación CSV |
/transshipment | Patrones de escalas en puertos Rusia↔Occidente — eventos RU→OCCIDENTE y OCCIDENTE→RU, exportación CSV |
/log/download | Descarga del archivo de registro sin procesar |
| Componente | Estado |
|---|
| Consumidor de flujo AIS | Funcionando |
| Lista de vigilancia | Más de 1200 embarcaciones (catálogo GUR completo) |
| Alerta de proximidad a cable | Funcionando |
| Detección de merodeo | Funcionando |
| Detección de transbordos | Funcionando — escalas inferidas de posición, ventana de 21 días |
| Renderizado de mapa | Funcionando — cadencia de 3 min, cuenta regresiva en vivo, recarga automática |
| Reinicio en caliente | Funcionando — pre-puebla el mapa desde los últimos 24h de registros al inicio |
| Ventanas emergentes de embarcaciones | Enlaces directos a MarineTraffic + VesselFinder + War&Sanctions (1337 embarcaciones mapeadas) |
| Ventanas emergentes de embarcaciones | Estado de sanciones de OpenSanctions (opcional, requiere clave API) |
| Detección de cambios de bandera | Funcionando — registra cambios, almacenados en flag_history |
| Panel — registros, embarcaciones, exportación CSV | Funcionando |
| Panel — vista de cronología | Funcionando |
| Panel — historial por embarcación + GPX | Funcionando |
| Panel — reproducción de ruta | Funcionando — reproducción/pausa/reinicio, control de velocidad, resaltado de alertas de cable |
| Panel — panel de merodeo | Funcionando |
| Panel — panel de transbordos | Funcionando |