
Rastreador open source de frota fantasma do Mar Báltico. Mais de 1200 embarcações, AIS ao vivo, alertas de proximidade de cabos. Sem nuvem, sem assinatura, executa localmente
Monitoramento da frota sombra do Mar Báltico através de dados AIS ao vivo. Observa mais de 1200 embarcações do catálogo War&Sanctions da GUR ucraniana contra o feed WebSocket do AISStream, plota posições em um mapa auto-atualizável, sinaliza proximidade de cabos submarinos e detecta padrões de transbordo Rússia↔Ocidente.
Gratuito, código aberto, executa localmente. Sem nuvem, sem assinatura além de uma chave de API gratuita do AISStream.
Parte da cadeia de ferramentas de inteligência soberana do Former Lab.
O Shadow Fleet Tracker Light é construído e mantido pela equipe do Former Lab — computação soberana, construindo ferramentas abertas e focadas em privacidade em hardware antigo sem apoio de capital de risco.
Se esta ferramenta é útil para você, considere apoiar no Patreon. Um plano gratuito está disponível, com teste de 7 dias nos planos pagos. Apoie sempre a Ucrânia!
Postagem no blog sobre o rastreador, capturas de tela e mais: 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
Abra http://localhost:8000 — o mapa está em /map.
Usuários do Windows: veja a seção Configuração para a sintaxe de variável de ambiente no PowerShell/cmd.
Usuários Linux/macOS podem usar start.sh. Usuários do Windows podem usar start.bat ou start.ps1 — ambos abrem o rastreador e o painel em janelas separadas e iniciam o navegador automaticamente.
Vessels1.db)filtered_cables.kml)Mapa base escuro do CartoDB com rotas de cabos sobrepostas em verde. Marcadores de embarcações são atualizados a cada 3 minutos com uma contagem regressiva ao vivo para a próxima atualização — a página recarrega automaticamente. Popups mostram MMSI, IMO, bandeira, velocidade, rumo, destino e links diretos para MarineTraffic, VesselFinder e War&Sanctions. Status de sanções do OpenSanctions exibido se uma chave de API estiver configurada.
Os nomes conhecidos de embarcações de Vessels1.db são exibidos imediatamente na primeira aparição, sem esperar por uma mensagem estática AIS.
Na inicialização, o mapa é pré-preenchido com as últimas 24 horas do registro de posição. Embarcações ainda não vistas na sessão atual são mostradas como marcadores cinza com um rótulo 'visto pela última vez Xh atrás' — elas transitam para cor viva à medida que novos pings chegam.
shadow_tracker.py Main process — WS consumer, map renderer, DB writes
loitering_module.py Loitering detection and map annotation
transshipment_module.py Port call detection and Russia↔West transshipment flagging
webserver.py FastAPI dashboard
gur_scrape.py One-shot scraper — builds IMO→GUR-ID mapping + full vessel catalogue
check.py Preflight checks — Python version, deps, API key, network
start.sh Launcher for Linux / macOS
start.bat Launcher for Windows (Command Prompt)
start.ps1 Launcher for Windows (PowerShell)
requirements.txt Dependencies
Vessels1.db Watchlist — 1200+ vessels (MMSI + IMO + name where known)
vessel_data_log.db Position log — runtime, append-only
vessel_static.db AIS static data cache (name, destination, flag history)
transshipment.db Port call log and transshipment events
loitering_events.db Loitering events
filtered_cables.kml Baltic Sea undersea cable geometry
cables.kml Full cable dataset (pre-filter source)
gur_mapping.json IMO→GUR-ID mapping (produced by gur_scrape.py, optional)
gur_vessels_full.json Full GUR catalogue — GUR-ID→{imo, mmsi, name, flag} (produced by gur_scrape.py)
Vessels1.db é a lista canônica de embarcações rastreadas. Mais de 1200 entradas provenientes do catálogo War&Sanctions da GUR ucraniana, cobrindo petroleiros e cargueiros identificados como parte da frota sombra russa ou implicados em atividades híbridas no Báltico.
A lista inclui embarcações recentemente apreendidas ou interceptadas por autoridades do Báltico:
Os MMSIs são corrigidos com base nos dados AIS atuais — as embarcações da frota sombra mudam de bandeira com frequência. O arquivo update_vessels_2026_03.sql documenta todas as alterações com fontes.
A lista de observação é intencionalmente aberta. Adicione embarcações diretamente ao Vessels1.db enquanto o rastreador estiver em execução — elas serão detectadas em até 5 minutos sem necessidade de reinicialização.
Linux / macOS:
sqlite3 Vessels1.db "INSERT OR IGNORE INTO vessels (mmsi, imo, name) VALUES ('123456789', '9999999', 'VESSEL NAME');"
Windows (no sqlite3 CLI needed):
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 observação
vessels(mmsi TEXT PK, imo TEXT, name TEXT, destination TEXT)
vessel_data_log.db — registro de posição
vessel_data_log(timestamp, mmsi, name, imo, destination, speed, heading, latitude, longitude, cable_alert)
Somente anexação. Escrito a cada ciclo de renderização do mapa.
vessel_static.db — cache de dados estáticos AIS
vessel_static(mmsi TEXT PK, name TEXT, imo TEXT, destination TEXT, flag TEXT)
flag_history(id, mmsi, flag, timestamp)
Preenchido a partir de mensagens AIS ShipStaticData ao vivo. Tem prioridade sobre os nomes de Vessels1.db uma vez recebido. flag_history registra cada mudança de bandeira com um timestamp — eventos de mudança de bandeira são registrados e exibidos na página por embarcação.
loitering_events.db — registro de navegação lenta (criado em tempo de execução)
loitering_events(id, mmsi, timestamp, latitude, longitude, near_cable)
transshipment.db — registro de escalas e transbordo (criado em tempo de execução)
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.txtA maneira mais fácil de executar o rastreador. Cada script instala dependências, executa verificações preliminares e inicia ambos os processos.
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
Todos os três scripts executam check.py primeiro — uma verificação preliminar que verifica a versão do Python, dependências, arquivos de dados, chave de API e acessibilidade de rede, com mensagens de erro claras se algo estiver faltando.
Você também pode executar a verificação preliminar por conta própria a qualquer momento:
python check.py
1. Clone e instale dependências
git clone https://github.com/FormerLab/shadow-fleet-tracker-light.git
cd shadow-fleet-tracker-light
pip install -r requirements.txt
No Ubuntu 22.04+ e Debian 12+ você pode obter um erro de "ambiente gerenciado externamente" do pip. Os scripts de inicialização lidam com isso automaticamente criando um ambiente virtual — basta executar start.sh e ele cuida disso. Se instalar manualmente, crie um venv primeiro:
python3 -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
pip install -r requirements.txt
2. Defina sua chave de API AISStream
Linux / macOS:
export AISSTREAM_API_KEY=your_key_here
export OPENSANCTIONS_API_KEY=your_key_here # opcional
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. Execute o rastreador e o painel em dois terminais separados
Terminal 1 — o consumidor do feed AIS:
python shadow_tracker.py
Terminal 2 — o painel web:
uvicorn webserver:app --host 0.0.0.0 --port 8000
4. Abra o painel
http://localhost:8000
O mapa ao vivo está em http://localhost:8000/map e atualiza a cada 3 minutos. Na primeira execução, o mapa é renderizado imediatamente a partir de qualquer histórico de posição já no banco de dados — sem espera em tela em branco.
| Serviço | Obrigatório | Plano gratuito | Link |
|---|---|---|---|
| AISStream | Sim | Sim — ilimitado para uso não comercial | aisstream.io |
Sem OPENSANCTIONS_API_KEY o rastreador funciona normalmente — os selos de sanções são simplesmente omitidos dos popups do mapa.
Todos os parâmetros ajustáveis estão no topo de shadow_tracker.py:
Variáveis de ambiente:
| Variável | Obrigatório | Descrição |
|---|---|---|
AISSTREAM_API_KEY | Sim | Chave de API WebSocket do AISStream |
OPENSANCTIONS_API_KEY | Não | Habilita consulta de sanções nos popups do mapa |
OpenSanctions é gratuito para uso não comercial — registre-se em opensanctions.org/api. Sem uma chave, o rastreador funciona normalmente; campos de sanções são omitidos dos popups.
war-sanctions.gur.gov.ua é o catálogo público da GUR ucraniana de embarcações da frota sombra e sancionadas. Cada embarcação tem uma página numerada com histórico de escalas, mapas e detalhes de sanções.
gur_scrape.py constrói um mapeamento local IMO→GUR-ID ao rastrear o catálogo uma vez:
python gur_scrape.py
Isso produz gur_mapping.json. O rastreador o carrega na inicialização e usa links diretos (/en/transport/ships/{id}) nos popups do mapa quando disponíveis, recorrendo a uma pesquisa no site do Google para embarcações ainda não no catálogo.
O rastreamento cobre ~1600 entradas a 1,5 s/requisição (~40 min). Reexecute ocasionalmente conforme o catálogo cresce — use --start para retomar de um ID específico:
python gur_scrape.py --start 1580
Para depurar uma única página:
python gur_scrape.py --probe 1517
gur_mapping.json é recarregado automaticamente a cada 5 minutos junto com a lista de observação — sem necessidade de reinicialização do rastreador após um novo rastreamento.
Limiares de navegação lenta em loitering_module.py:
| Constante | Padrão | Descrição |
|---|---|---|
SPEED_THRESHOLD_KN |
Configurações de transbordo em transshipment_module.py:
| Constante | Padrão | Descrição |
|---|---|---|
SPEED_THRESHOLD_KN |
Zonas portuárias cobertas:
| Porto | Tipo |
|---|---|
| Ust-Luga, Primorsk, São Petersburgo, Vyborg | Terminais de exportação russos |
| Skaw/Skagen, Gotemburgo, Kiel, Copenhague, Aarhus | Hubs de transbordo ocidentais |
Cada página de embarcação (/vessel/<mmsi>) mostra:
Sinaliza dois padrões inferidos a partir de dados de posição:
As escalas de porto são inferidas a partir de pings de posição — nenhuma API externa necessária. Uma embarcação deve estar dentro de uma zona portuária a <1,5 kn por pelo menos 2 pings para registrar uma escala. Isso mostra um padrão, não um veredicto.
filtered_cables.kml é derivado de conjuntos de dados de código aberto e é aproximado — não adequado para cálculos precisos de proximidade. O limiar de alerta de 10 km leva em conta essa margem. Contribuições de geometria de cabo melhorada são bem-vindas.
O rastreador executa um único loop assíncrono sobre o fluxo WebSocket. O estado é mantido em memória (vessel_info, static_cache) e gravado no SQLite a cada ciclo de renderização. O mapa é escrito atomicamente via os.replace para evitar servir um arquivo parcial.
Na inicialização, warm_restart() lê as últimas WARM_RESTART_HOURS horas de vessel_data_log.db e pré-popula vessel_info antes que o WebSocket conecte. O mapa inicial é renderizado imediatamente. Posições obsoletas são visualmente diferenciadas (marcadores cinza, trilhas atenuadas) e transitam para cor viva à medida que novos pings chegam.
A lista de observação e gur_mapping.json são recarregados do disco a cada 5 minutos — embarcações podem ser adicionadas e o mapeamento GUR pode ser rastreado novamente enquanto o rastreador está em execução, sem reinicialização.
O servidor web é stateless e lê diretamente dos arquivos SQLite — sem memória compartilhada com o processo do rastreador.
| Embarcação | IMO | Evento |
|---|
| EAGLE S | 9329760 | Finlândia apreendeu Dez 2024 — sabotagem do cabo Estlink-2, equipamento de espionagem encontrado |
| EVENTIN | 9308065 | Alemanha apreendeu Jan–Mar 2025 — derivou perto de Rügen, 100.000t de petróleo bruto russo confiscado |
| KIWALA | 9332810 | Estônia deteve Abr 2025 — sem bandeira, sancionado pela UE/Reino Unido |
| JAGUAR | 9293002 | Estônia interceptou Mai 2025 — Rússia mobilizou Su-35 para escoltá-lo |
| KIRA K | 9346720 | Tripulação Wagner/GRU confirmada a bordo Dez 2025 |
| QENDIL | 9310525 | Tripulação Wagner/GRU Set 2025 — ataque de drone ucraniano Dez 2025 |
| FITBURG | 9250397 | Finlândia apreendeu 31 Dez 2025 — sabotagem do cabo Helsinque-Tallinn |
| CAFFA | 9143611 | Suécia apreendeu 6 Mar 2026 — grão ucraniano roubado, bandeira falsa |
| SEA OWL I | 9321172 | Suécia apreendeu 12 Mar 2026 — sancionado pela UE, bandeira falsa de Comores |
| OpenSanctions | Não | Sim — uso não comercial | opensanctions.org/api |
| Constante | Padrão | Descrição |
|---|
BALTIC_BBOX | 52.65–66°N, 9–30°E | Caixa delimitadora de assinatura do AISStream |
CABLE_ALERT_KM | 10 | Limiar de proximidade de cabo |
MAP_RENDER_INTERVAL | 180 s | Cadência de atualização do mapa |
MMSI_RELOAD_INTERVAL | 300 s | Recarregamento da lista de observação do BD |
WARM_RESTART_HOURS | 24 | Horas de histórico de posição para carregar na inicialização |
RECONNECT_DELAY | 10 s | Backoff de reconexão WS |
| 0,5 kn |
| Abaixo disso conta como parado |
TIME_THRESHOLD_S | 1200 s | Duração antes de sinalizar navegação lenta |
RADIUS_THRESHOLD_KM | 0,5 km | Desvio máximo para ainda contar como mesmo local |
| 1,5 kn |
| Abaixo disso dentro de uma zona portuária conta como escala |
MIN_PINGS_IN_ZONE | 2 | Pings mínimos antes de registrar uma escala |
WINDOW_DAYS | 21 | Dias máximos entre escalas para sinalizar como transbordo |
| Rota | Descrição |
|---|
/ | Visualizador de logs ao vivo, atualização automática a cada 60 s |
/map | Mapa renderizado mais recente (compatível com iframe) |
/analyze | Tabela de registros de embarcações filtrável com exportação CSV |
/timeline | Visão geral da atividade — um cartão por embarcação rastreada, ordenado pela última visualização |
/vessel/<mmsi> | Histórico por embarcação: apagões AIS, proximidade de cabo, mudanças de bandeira, exportação GPX |
/vessel/<mmsi>/gpx | Exportação de trilha GPX — abre no QGIS, OpenStreetMap, GPSBabel |
/loitering | Eventos de navegação lenta com sinalização de proximidade de cabo, resumo por embarcação, exportação CSV |
/transshipment | Padrões de escalas de porto Rússia↔Ocidente — eventos RU→WEST e WEST→RU, exportação CSV |
/log/download | Download do arquivo de log bruto |
| Componente | Estado |
|---|
| Consumidor de fluxo AIS | Funcionando |
| Lista de observação | Mais de 1200 embarcações (catálogo GUR completo) |
| Alerta de proximidade de cabo | Funcionando |
| Detecção de navegação lenta | Funcionando |
| Detecção de transbordo | Funcionando — escalas inferidas da posição, janela de 21 dias |
| Renderização do mapa | Funcionando — cadência de 3 min, contagem regressiva ao vivo, recarga automática |
| Reinicialização a quente | Funcionando — pré-preenche mapa com últimas 24h de logs na inicialização |
| Popups de embarcações | Links diretos MarineTraffic + VesselFinder + War&Sanctions (1337 embarcações mapeadas) |
| Popups de embarcações | Status de sanções OpenSanctions (opcional, requer chave de API) |
| Detecção de mudança de bandeira | Funcionando — registra mudanças, armazenado em flag_history |
| Painel — log, embarcações, exportação CSV | Funcionando |
| Painel — visão de linha do tempo | Funcionando |
| Painel — histórico por embarcação + GPX | Funcionando |
| Painel — reprodução de rota | Funcionando — play/pause/reset, controle de velocidade, destaque de alerta de cabo |
| Painel — painel de navegação lenta | Funcionando |
| Painel — painel de transbordo | Funcionando |