
オープンソースのバルト海シャドーフリートトラッカー。1200隻以上の船舶、ライブAIS、ケーブル接近アラート。クラウド不要、サブスクリプション不要、ローカルで動作。
バルト海の影の艦隊(Shadow Fleet)を、リアルタイムAISデータで監視します。ウクライナGUR(国防省情報総局)の戦争・制裁カタログに掲載された1200隻以上の船舶を、AISStream WebSocketフィードと照合し、自己更新地図上に位置をプロット。海底ケーブルへの接近を警告し、ロシア↔西側のトランシップメント(積替え)パターンを検出します。
無料・オープンソース、ローカルで動作します。クラウド不要、無料のAISStream APIキー以外にサブスクリプションは不要です。
Former Lab 主権インテリジェンスツールチェーンの一部です。
Shadow Fleet Tracker Light は Former Lab チームによって構築・維持されています — 主権的コンピューティング、古いハードウェア上でオープンでプライバシー第一のツールを、VCの支援なしで構築しています。
このツールが役に立つなら、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)から取得した1200隻以上の船舶MMSIのウォッチリストとストリームを照合filtered_cables.kml から)から10km以内に接近した場合に警告ダークCartoDBベースマップに海底ケーブルルートを緑色で重ね表示。船舶マーカーは3分ごとに更新され、次の更新までのライブカウントダウン表示 — ページは自動リロードされます。ポップアップではMMSI、IMO、船籍国、速度、針路、目的地、MarineTraffic・VesselFinder・War&Sanctionsへのディープリンクを表示。OpenSanctions APIキーが設定されている場合は制裁ステータスも表示。
Vessels1.db に既知の船名がある場合、AIS静的情報メッセージを待たずに最初の出現時に即座に表示されます。
起動時、地図は過去24時間分の位置ログから事前投入されます。現在のセッションでまだ見られていない船舶は灰色のマーカーと「最終確認 X時間前」のラベルで表示 — 新しいpingが届くとライブカラーに遷移します。
shadow_tracker.py メインプロセス — WSコンシューマー、地図レンダラー、DB書き込み
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用ランチャー(コマンドプロンプト)
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カタログ — GUR-ID→{imo, mmsi, name, flag}(gur_scrape.pyで生成)
Vessels1.db は追跡対象船舶の標準リストです。1200以上のエントリがウクライナGUR戦争・制裁カタログから取得されており、ロシアの影の艦隊の一部として特定されたタンカーや貨物船、またはバルト海でのハイブリッド活動に関与した船舶をカバーしています。
リストには、バルト諸国が最近拿捕または阻止した船舶も含まれます:
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', 'VESSEL NAME');"
Windows(sqlite3 CLI不要):
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 — ウォッチリスト
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)
ライブの ShipStaticData AISメッセージから取り込まれます。受信後は 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(コマンドプロンプト):
set AISSTREAM_API_KEY=your_key_here
start.bat
3つのスクリプトはすべて最初に 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+ では、pipから「externally managed environment」エラーが発生する場合があります。起動スクリプトは仮想環境を作成して自動的に処理します — start.sh を実行するだけです。手動でインストールする場合は、事前にvenvを作成してください:
python3 -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
pip install -r requirements.txt
2. AISStream APIキーの設定
Linux / macOS:
export AISSTREAM_API_KEY=your_key_here
export OPENSANCTIONS_API_KEY=your_key_here # 任意
Windows(コマンドプロンプト):
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. トラッカーとダッシュボードを2つのターミナルで実行
ターミナル1 — AISフィードコンシューマー:
python shadow_tracker.py
ターミナル2 — Webダッシュボード:
uvicorn webserver:app --host 0.0.0.0 --port 8000
4. ダッシュボードを開く
http://localhost:8000
ライブ地図は http://localhost:8000/map にあり、3分ごとに更新されます。初回実行時は、データベース内の位置履歴から即座に地図がレンダリングされます — 白紙待機なし。
| サービス | 必須 | 無料枠 | リンク |
|---|---|---|---|
| AISStream | はい | はい — 非商用は無制限 | aisstream.io |
| OpenSanctions |
OPENSANCTIONS_API_KEY がなくてもトラッカーは正常に動作します — 制裁バッジが地図ポップアップに表示されないだけです。
すべての調整可能な定数は shadow_tracker.py の先頭にあります:
環境変数:
| 変数 | 必須 | 説明 |
|---|---|---|
AISSTREAM_API_KEY | はい | AISStream WebSocket APIキー |
OPENSANCTIONS_API_KEY | いいえ | 地図ポップアップの制裁情報表示を有効化 |
OpenSanctionsは非商用利用は無料です — opensanctions.org/api で登録してください。キーがなくてもトラッカーは正常に動作します。制裁フィールドはポップアップに表示されません。
war-sanctions.gur.gov.ua はウクライナGURによる影の艦隊および制裁対象船舶の公開カタログです。各船舶には番号付きページがあり、寄港履歴、地図、制裁詳細が記載されています。
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 の滞留閾値:
| 定数 | デフォルト値 | 説明 |
|---|---|---|
SPEED_THRESHOLD_KN | 0.5ノット | これを下回ると停泊とみなす |
transshipment_module.py のトランシップメント設定:
| 定数 | デフォルト値 | 説明 |
|---|---|---|
SPEED_THRESHOLD_KN | 1.5ノット | 港ゾーン内でこれを下回ると寄港とみなす |
カバーする港ゾーン:
| 港 | タイプ |
|---|---|
| ウスチルガ、プリモルスク、サンクトペテルブルク、ヴィボルグ | ロシアの輸出ターミナル |
| スカーウ/スカーゲン、ヨーテボリ、キール、コペンハーゲン、オーフス | 西側トランシップメントハブ |
各船舶ページ(/vessel/<mmsi>)には以下が表示されます:
位置データから推測される2つのパターンをフラグ付けします:
寄港は位置pingから推測されます — 外部APIは不要です。船舶が港ゾーン内で1.5ノット未満で少なくとも2回のpingを記録する必要があります。これはパターンを示すものであり、断定ではありません。
filtered_cables.kml はオープンソースデータセットから派生したもので、概算です — 正確な近接計算には適していません。10kmの警報閾値はこの誤差を考慮しています。改良されたケーブル形状データの提供を歓迎します。
トラッカーはWebSocketストリーム上で単一の非同期ループを実行します。状態はメモリ内(vessel_info、static_cache)に保持され、各レンダリングサイクルでSQLiteにフラッシュされます。地図は os.replace を使用してアトミックに書き込まれ、部分的なファイルの配信を防ぎます。
起動時、warm_restart() は vessel_data_log.db から過去 WARM_RESTART_HOURS 時間分を読み取り、WebSocket接続前に vessel_info に事前投入します。初回の地図は即座にレンダリングされます。古い位置は視覚的に区別され(灰色のマーカー、薄い航跡)、新しいpingが届くとライブカラーに遷移します。
ウォッチリストと gur_mapping.json は5分ごとにディスクから再読み込みされます — トラッカー実行中に船舶を追加したりGURマッピングを再クロールしたりでき、再起動は不要です。
ウェブサーバーはステートレスで、SQLiteファイルから直接読み取ります — トラッカープロセスとの共有メモリはありません。
| 船舶名 | IMO | 出来事 |
|---|
| EAGLE S | 9329760 | 2024年12月フィンランドが拿捕 — Estlink-2ケーブル妨害、スパイ機器発見 |
| EVENTIN | 9308065 | 2025年1月~3月ドイツが押収 — リューゲン島沖で漂流、10万トンのロシア原油を没収 |
| KIWALA | 9332810 | 2025年4月エストニアが拘留 — 船籍なし、EU/英国制裁対象 |
| JAGUAR | 9293002 | 2025年5月エストニアが阻止 — ロシアがSu-35をスクランブルして護衛 |
| KIRA K | 9346720 | 2025年12月、ワグナー/GRU乗組員確認 |
| QENDIL | 9310525 | 2025年9月ワグナー/GRU乗組員 — 2025年12月ウクライナドローン攻撃 |
| FITBURG | 9250397 | 2025年12月31日フィンランドが拿捕 — ヘルシンキ-タリン間ケーブル妨害 |
| CAFFA | 9143611 | 2026年3月6日スウェーデンが拿捕 — 盗難ウクライナ穀物、偽旗 |
| SEA OWL I | 9321172 | 2026年3月12日スウェーデンが拿捕 — EU制裁対象、偽のコモロ船籍 |
| いいえ |
| はい — 非商用利用 |
| opensanctions.org/api |
| 定数 | デフォルト値 | 説明 |
|---|
BALTIC_BBOX | 北緯52.65~66°, 東経9~30° | AISStream購読バウンディングボックス |
CABLE_ALERT_KM | 10 | ケーブル接近閾値(km) |
MAP_RENDER_INTERVAL | 180秒 | 地図更新間隔 |
MMSI_RELOAD_INTERVAL | 300秒 | ウォッチリストDB再読み込み間隔 |
WARM_RESTART_HOURS | 24 | 起動時に読み込む位置履歴の時間数 |
RECONNECT_DELAY | 10秒 | WebSocket再接続のバックオフ時間 |
TIME_THRESHOLD_S| 1200秒 |
| 滞留と判定されるまでの時間 |
RADIUS_THRESHOLD_KM | 0.5km | 同じ場所とみなす最大漂流距離 |
MIN_PINGS_IN_ZONE| 2 |
| 寄港記録に必要な最低ping数 |
WINDOW_DAYS | 21 | トランシップメントと判定する寄港間の最大日数 |
| ルート | 説明 |
|---|
/ | ライブログビューア、60秒ごとに自動更新 |
/map | 最新レンダリング地図(iframe対応) |
/analyze | フィルタ可能な船舶記録テーブル、CSVエクスポート付き |
/timeline | アクティビティ概要 — 追跡船舶ごとに1カード、最終確認順にソート |
/vessel/<mmsi> | 船舶ごとの履歴:AIS通信途絶、ケーブル接近、船籍変更、GPXエクスポート |
/vessel/<mmsi>/gpx | GPXトラックエクスポート — QGIS、OpenStreetMap、GPSBabelで開く |
/loitering | 滞留イベント(ケーブル接近フラグ付き)、船舶別サマリー、CSVエクスポート |
/transshipment | ロシア↔西側の寄港パターン — RU→WESTおよびWEST→RUイベント、CSVエクスポート |
/log/download | 生ログファイルのダウンロード |
| コンポーネント | 状態 |
|---|
| AISストリームコンシューマー | 動作中 |
| ウォッチリスト | 1200隻以上(完全なGURカタログ) |
| ケーブル接近警報 | 動作中 |
| 滞留検出 | 動作中 |
| トランシップメント検出 | 動作中 — 位置から寄港を推測、21日間ウィンドウ |
| 地図レンダリング | 動作中 — 3分間隔、ライブカウントダウン、自動リロード |
| ウォームリスタート | 動作中 — 起動時に過去24時間のログから地図に事前投入 |
| 船舶ポップアップ | MarineTraffic + VesselFinder + War&Sanctions 直接リンク(1337隻マッピング済み) |
| 船舶ポップアップ | OpenSanctions制裁ステータス(任意、APIキーが必要) |
| 船籍変更検出 | 動作中 — 変更をログに記録、flag_history に保存 |
| ダッシュボード — ログ、船舶、CSVエクスポート | 動作中 |
| ダッシュボード — タイムラインビュー | 動作中 |
| ダッシュボード — 船舶履歴とGPX | 動作中 |
| ダッシュボード — 航路再生 | 動作中 — 再生/一時停止/リセット、速度調整、ケーブル警告ハイライト |
| ダッシュボード — 滞留パネル | 動作中 |
| ダッシュボード — トランシップメントパネル | 動作中 |