
Трекер теневого флота Балтийского моря с открытым исходным кодом. 1200+ судов, живые данные AIS, оповещения о приближении к кабелям. Никаких облаков, никакой подписки, работает локально.
Мониторинг теневого флота Балтийского моря через живые данные AIS. Отслеживает 1200+ судов из каталога War&Sanctions ГУР Украины через WebSocket-канал AISStream, отображает позиции на самообновляющейся карте, отмечает близость к подводным кабелям и обнаруживает паттерны перевалки грузов Россия↔Запад.
Бесплатно, с открытым исходным кодом, работает локально. Никакого облака, подписка не требуется, кроме бесплатного API-ключа AISStream.
Часть инструментария суверенной разведки Former Lab.
Shadow Fleet Tracker Light создан и поддерживается командой Former Lab — суверенные вычисления, создание открытых инструментов, ориентированных на конфиденциальность, на старом оборудовании, без венчурного финансирования.
Если этот инструмент оказался вам полезен, подумайте о поддержке на Patreon. Доступен бесплатный тариф с 7-дневной пробной версией платных тарифов. Всегда поддерживайте Украину!
Статья в блоге о трекере, скриншоты и подробности: 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 # бесплатно на aisstream.io
python shadow_tracker.py &
uvicorn webserver:app --host 0.0.0.0 --port 8000
Откройте http://localhost:8000 — карта доступна по адресу /map.
Пользователи Windows: см. раздел Настройка для синтаксиса переменных окружения в PowerShell/cmd.
Пользователи Linux / macOS могут использовать start.sh. Пользователи Windows — start.bat или start.ps1 — оба скрипта открывают трекер и панель в отдельных окнах и запускают браузер автоматически.
Vessels1.db)filtered_cables.kml)Тёмная базовая карта CartoDB с наложенными кабельными маршрутами зелёным цветом. Маркеры судов обновляются каждые 3 минуты с живым обратным отсчётом до следующего обновления — страница перезагружается автоматически. Всплывающие окна показывают MMSI, IMO, флаг, скорость, курс, пункт назначения и глубокие ссылки на MarineTraffic, VesselFinder и War&Sanctions. Статус санкций OpenSanctions отображается, если настроен API-ключ.
Известные названия судов из Vessels1.db отображаются сразу при первом появлении, без ожидания статического сообщения AIS.
При запуске карта заполняется данными за последние 24 часа из журнала позиций. Суда, ещё не замеченные в текущем сеансе, отображаются серыми маркерами с пометкой «последний раз видели Xh назад» — они переходят в живой цвет по мере поступления новых пингов.
shadow_tracker.py Основной процесс — потребитель WS, рендеринг карты, запись в БД
loitering_module.py Обнаружение длительного нахождения и аннотация карты
transshipment_module.py Обнаружение заходов в порты и маркировка перевалки Россия↔Запад
webserver.py Панель управления FastAPI
gur_scrape.py Одноразовый скрапер — построение отображения IMO→GUR-ID + полный каталог судов
check.py Предстартовые проверки — версия Python, зависимости, API-ключ, сеть
start.sh Запускатор для Linux / macOS
start.bat Запускатор для Windows (Command Prompt)
start.ps1 Запускатор для Windows (PowerShell)
requirements.txt Зависимости
Vessels1.db Список наблюдения — 1200+ судов (MMSI + IMO + название, где известно)
vessel_data_log.db Журнал позиций — во время выполнения, только добавление
vessel_static.db Кэш статических данных AIS (название, пункт назначения, история флага)
transshipment.db Журнал заходов в порты и событий перевалки
loitering_events.db События длительного нахождения
filtered_cables.kml Геометрия подводных кабелей Балтийского моря
cables.kml Полный набор данных о кабелях (исходный источник до фильтрации)
gur_mapping.json Отображение IMO→GUR-ID (создаётся gur_scrape.py, опционально)
gur_vessels_full.json Полный каталог ГУР — GUR-ID→{imo, mmsi, name, flag} (создаётся gur_scrape.py)
Vessels1.db — канонический список отслеживаемых судов. Более 1200 записей из каталога War&Sanctions ГУР Украины, охватывающих танкеры и грузовые суда, идентифицированные как часть российского теневого флота или причастные к гибридной деятельности в Балтийском море.
Список включает суда, недавно захваченные или перехваченные балтийскими властями:
MMSI скорректированы по текущим данным AIS — суда теневого флота часто меняют флаг. Файл update_vessels_2026_03.sql документирует все изменения с указанием источников.
Список наблюдения намеренно открыт. Добавляйте суда напрямую в Vessels1.db во время работы трекера — они будут подхвачены в течение 5 минут без перезапуска.
Linux / macOS:
sqlite3 Vessels1.db "INSERT OR IGNORE INTO vessels (mmsi, imo, name) VALUES ('123456789', '9999999', 'НАЗВАНИЕ СУДНА');"
Windows (без CLI sqlite3):
import sqlite3
conn = sqlite3.connect("Vessels1.db")
conn.execute("INSERT OR IGNORE INTO vessels (mmsi, imo, name) VALUES ('123456789', '9999999', 'НАЗВАНИЕ СУДНА')")
conn.commit()
conn.close()
Vessels1.db — список наблюдения
vessels(mmsi TEXT PK, imo TEXT, name TEXT, destination TEXT)
vessel_data_log.db — журнал позиций
vessel_data_log(timestamp, mmsi, name, imo, destination, speed, heading, latitude, longitude, cable_alert)
Только добавление. Записывается при каждом цикле рендеринга карты.
vessel_static.db — кэш статических данных AIS
vessel_static(mmsi TEXT PK, name TEXT, imo TEXT, destination TEXT, flag TEXT)
flag_history(id, mmsi, flag, timestamp)
Заполняется из живых сообщений AIS ShipStaticData. Приоритет выше, чем у названий из Vessels1.db, после получения. flag_history записывает каждую смену флага с отметкой времени — события перерегистрации флага регистрируются и отображаются на странице конкретного судна.
loitering_events.db — журнал длительного нахождения (создаётся во время выполнения)
loitering_events(id, mmsi, timestamp, latitude, longitude, near_cable)
transshipment.db — журнал заходов в порты и перевалки (создаётся во время выполнения)
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.txtСамый простой способ запустить трекер. Каждый скрипт устанавливает зависимости, выполняет предстартовые проверки и запускает оба процесса.
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
Все три скрипта сначала запускают check.py — предстартовую проверку, которая проверяет версию Python, зависимости, файлы данных, API-ключ и доступность сети, выводя понятные сообщения об ошибках, если чего-то не хватает.
Вы также можете запустить предстартовую проверку отдельно в любое время:
python check.py
1. Клонирование и установка зависимостей
git clone https://github.com/FormerLab/shadow-fleet-tracker-light.git
cd shadow-fleet-tracker-light
pip install -r requirements.txt
На Ubuntu 22.04+ и Debian 12+ может появиться ошибка «externally managed environment» от pip. Скрипты запуска обрабатывают это автоматически, создавая виртуальное окружение — просто запустите start.sh, и он все сделает. Если устанавливаете вручную, сначала создайте виртуальное окружение:
python3 -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
pip install -r requirements.txt
2. Установите API-ключ AISStream
Linux / macOS:
export AISSTREAM_API_KEY=your_key_here
export OPENSANCTIONS_API_KEY=your_key_here # опционально
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. Запустите трекер и панель в двух отдельных терминалах
Терминал 1 — потребитель AIS-потока:
python shadow_tracker.py
Терминал 2 — веб-панель:
uvicorn webserver:app --host 0.0.0.0 --port 8000
4. Откройте панель управления
http://localhost:8000
Живая карта доступна по адресу http://localhost:8000/map и обновляется каждые 3 минуты. При первом запуске карта отображается немедленно из любой уже имеющейся в базе истории позиций — без ожидания.
| Сервис | Обязательный | Бесплатный тариф | Ссылка |
|---|---|---|---|
| AISStream | Да | Да — безлимитно для некоммерческого использования |
Без OPENSANCTIONS_API_KEY трекер работает в обычном режиме — значки санкций просто не отображаются во всплывающих окнах карты.
Все настраиваемые параметры находятся в начале файла shadow_tracker.py:
Переменные окружения:
| Переменная | Обязательная | Описание |
|---|---|---|
AISSTREAM_API_KEY | Да | API-ключ WebSocket AISStream |
OPENSANCTIONS_API_KEY | Нет | Включает проверку санкций во всплывающих окнах карты |
OpenSanctions бесплатен для некоммерческого использования — зарегистрируйтесь на opensanctions.org/api. Без ключа трекер работает в обычном режиме; поля санкций не отображаются во всплывающих окнах.
war-sanctions.gur.gov.ua — это публичный каталог ГУР Украины, содержащий суда теневого флота и находящиеся под санкциями. Каждое судно имеет пронумерованную страницу с историей заходов в порты, картами и сведениями о санкциях.
gur_scrape.py создаёт локальное отображение IMO→GUR-ID, однократно обходя каталог:
python gur_scrape.py
В результате создаётся gur_mapping.json. Трекер загружает его при запуске и использует прямые глубокие ссылки (/en/transport/ships/{id}) во всплывающих окнах карты, где это возможно, а для судов, ещё не добавленных в каталог, выполняет поиск по сайту Google.
Обход охватывает около 1600 записей с задержкой 1.5 с/запрос (~40 мин). Повторяйте время от времени по мере роста каталога — используйте --start, чтобы возобновить с определённого ID:
python gur_scrape.py --start 1580
Для отладки одной страницы:
python gur_scrape.py --probe 1517
gur_mapping.json перезагружается автоматически каждые 5 минут одновременно со списком наблюдения — перезапуск трекера после повторного обхода не требуется.
Пороги длительного нахождения в loitering_module.py:
| Константа | По умолчанию | Описание |
|---|---|---|
Настройки перевалки в transshipment_module.py:
| Константа | По умолчанию | Описание |
|---|---|---|
Охватываемые портовые зоны:
| Порт | Тип |
|---|---|
| Усть-Луга, Приморск, Санкт-Петербург, Выборг | Российские экспортные терминалы |
| Скау/Скаген, Гётеборг, Киль, Копенгаген, Орхус | Западные хабы перевалки |
На странице каждого судна (/vessel/<mmsi>) отображаются:
Отмечает два паттерна, выводимых из данных о позициях:
Заходы в порты выводятся из данных о позициях — внешний API не требуется. Судно должно находиться внутри портовой зоны со скоростью <1,5 узла как минимум для 2 пингов, чтобы заход был зарегистрирован. Это показывает паттерн, а не окончательный вердикт.
filtered_cables.kml получен из наборов данных с открытым исходным кодом и является приблизительным — не подходит для точных расчётов близости. Порог оповещения в 10 км учитывает эту погрешность. Приветствуются улучшения геометрии кабелей.
Трекер выполняет один асинхронный цикл поверх WebSocket-потока. Состояние хранится в памяти (vessel_info, static_cache) и сбрасывается в SQLite при каждом цикле рендеринга. Карта записывается атомарно через os.replace, чтобы избежать обслуживания частичного файла.
При запуске warm_restart() считывает последние WARM_RESTART_HOURS часов из vessel_data_log.db и предварительно заполняет vessel_info до подключения WebSocket. Начальная карта отображается немедленно. Устаревшие позиции визуально отличаются (серые маркеры, приглушённые треки) и переходят в живой цвет по мере поступления новых пингов.
Список наблюдения и gur_mapping.json перезагружаются с диска каждые 5 минут — суда можно добавлять, а отображение ГУР повторно обходить во время работы трекера без перезапуска.
Веб-сервер не имеет состояния и читает напрямую из файлов SQLite — общей памяти с процессом трекера нет.
| Судно | IMO | Событие |
|---|
| EAGLE S | 9329760 | Захвачено Финляндией, дек. 2024 — саботаж кабеля Estlink-2, найдено шпионское оборудование |
| EVENTIN | 9308065 | Захвачено Германией, янв.–мар. 2025 — дрейфовал у Рюгена, конфисковано 100 000 т российской нефти |
| KIWALA | 9332810 | Задержано Эстонией, апр. 2025 — без флага, под санкциями ЕС/Великобритании |
| JAGUAR | 9293002 | Перехвачено Эстонией, май 2025 — Россия подняла Су-35 для эскорта |
| KIRA K | 9346720 | Экипаж Wagner/ГРУ подтверждён на борту, дек. 2025 |
| QENDIL | 9310525 | Экипаж Wagner/ГРУ, сен. 2025 — удар украинского дрона, дек. 2025 |
| FITBURG | 9250397 | Захвачено Финляндией, 31 дек. 2025 — саботаж кабеля Хельсинки–Таллин |
| CAFFA | 9143611 | Захвачено Швецией, 6 мар. 2026 — украденное украинское зерно, ложный флаг |
| SEA OWL I | 9321172 | Захвачено Швецией, 12 мар. 2026 — под санкциями ЕС, ложный флаг Коморских островов |
| aisstream.io |
| OpenSanctions | Нет | Да — некоммерческое использование | opensanctions.org/api |
| Константа | Значение по умолчанию | Описание |
|---|
BALTIC_BBOX | 52.65–66°N, 9–30°E | Ограничивающий прямоугольник подписки AISStream |
CABLE_ALERT_KM | 10 | Порог близости к кабелю |
MAP_RENDER_INTERVAL | 180 с | Частота обновления карты |
MMSI_RELOAD_INTERVAL | 300 с | Перезагрузка списка наблюдения из БД |
WARM_RESTART_HOURS | 24 | Количество часов истории позиций для загрузки при запуске |
RECONNECT_DELAY | 10 с | Задержка повторного подключения к WebSocket |
SPEED_THRESHOLD_KN| 0.5 узла |
| Ниже этого значения считается остановкой |
TIME_THRESHOLD_S | 1200 с | Продолжительность, после которой фиксируется длительное нахождение |
RADIUS_THRESHOLD_KM | 0.5 км | Максимальный дрейф, чтобы считаться той же точкой |
SPEED_THRESHOLD_KN |
| 1.5 узла |
| Ниже этого значения внутри портовой зоны считается заходом |
MIN_PINGS_IN_ZONE | 2 | Минимальное количество пингов перед регистрацией захода |
WINDOW_DAYS | 21 | Максимальное количество дней между заходами для маркировки как перевалки |
| Маршрут | Описание |
|---|
/ | Просмотр журнала в реальном времени, автообновление каждые 60 с. |
/map | Последняя отрендеренная карта (совместимо с iframe) |
/analyze | Таблица записей судов с фильтрацией и экспортом CSV |
/timeline | Обзор активности — по одной карточке на отслеживаемое судно, отсортировано по последнему появлению |
/vessel/<mmsi> | История конкретного судна: отключения AIS, близость к кабелям, смены флага, экспорт GPX |
/vessel/<mmsi>/gpx | Экспорт GPX-трека — открывается в QGIS, OpenStreetMap, GPSBabel |
/loitering | События длительного нахождения с пометкой о близости к кабелю, сводка по судам, экспорт CSV |
/transshipment | Паттерны заходов Россия↔Запад — события RU→WEST и WEST→RU, экспорт CSV |
/log/download | Загрузка необработанного файла журнала |
| Компонент | Состояние |
|---|
| Потребитель AIS-потока | Работает |
| Список наблюдения | 1200+ судов (полный каталог ГУР) |
| Оповещение о близости к кабелю | Работает |
| Обнаружение длительного нахождения | Работает |
| Обнаружение перевалки | Работает — заходы в порты выводятся из позиций, окно 21 день |
| Рендеринг карты | Работает — интервал 3 мин, обратный отсчёт, автообновление |
| Тёплый перезапуск | Работает — предварительное заполнение карты из логов за последние 24 часа при запуске |
| Всплывающие окна судов | Прямые ссылки на MarineTraffic + VesselFinder + War&Sanctions (1337 судов сопоставлены) |
| Всплывающие окна судов | Статус санкций OpenSanctions (опционально, требуется API-ключ) |
| Обнаружение смены флага | Работает — фиксирует изменения, хранит в flag_history |
| Панель — журнал, суда, экспорт CSV | Работает |
| Панель — временная шкала | Работает |
| Панель — история судна + GPX | Работает |
| Панель — повтор маршрута | Работает — воспроизведение/пауза/сброс, управление скоростью, подсветка предупреждений о кабеле |
| Панель — страница длительного нахождения | Работает |
| Панель — страница перевалки | Работает |