A cloud-native catalog of Russia's maritime sanctions-evasion (“shadow”) fleet, built from Ukraine's Defence Intelligence (GUR) War & Sanctions portal.
Ukraine's Defence Intelligence (GUR) publishes the War & Sanctions portal to document the networks Russia uses to evade international sanctions. The shadow fleet (also “dark” or “ghost” fleet) is the armada of mostly ageing tankers used to move sanctioned Russian and Iranian oil outside Western oversight and the G7 oil price cap. GUR counts roughly 1,400 vessels — about 17% of the world's oil-tanker fleet, over 100 million DWT combined.
These ships share a recognisable pattern: flags of convenience, opaque single-vessel shell owners in jurisdictions like Hong Kong and the Seychelles, frequent name and flag changes, missing or dubious P&I insurance, and AIS transponders switched off to hide calls at Russian terminals. GUR links the fleet to more than 50 incidents from the Danish Straits to Malaysia since 2022, and warns of the environmental risk these poorly-maintained vessels pose in crowded sea lanes.
This catalog captures that portal as five linked, cloud-native tables — 1,404 vessels, 1,244 ports, 19,678 port calls, plus their companies and masters — as GeoParquet (with PMTiles for the mappable ones) and plain Parquet, described with STAC metadata so they can be queried in place with DuckDB or browsed online without a download.
Ship masters (captains) named in connection with the fleet.
vessels
🚢 1,404 rows · Geometry: MultiPoint — the ports each vessel is recorded as having visited
The core registry — one row per tanker GUR has flagged as part of the shadow fleet (~1,400 vessels). Each record carries vessel identity (IMO, MMSI, call sign, current and former names and flags), technical specs (type, length, gross tonnage, deadweight, build year and shipyard), the sanctions imposed and by whom, operational red flags (AIS shutdowns, calls at Russian ports, false identification data), and nested owner / commercial-manager / safety-manager company details. The geometry is a MultiPoint of the ports the vessel has visited.
Per-jurisdiction booleans (sanctioned_eu, sanctioned_us, sanctioned_uk, sanctioned_ua, sanctioned_ch, sanctioned_ca) make it easy to filter by who has designated a vessel, while sanctioned_by keeps the raw authority list. The ship_owner, commercial_manager and safety_manager columns are JSON objects holding a company name, IMO and country — pull fields with json_extract_string(...) and match their company_imo against the companies table. visited_port_ids links to ports / visits, and gur_id links to masters.
Type of vessel (e.g. crude oil tanker, bunkering tanker)
ports
⚓ 1,244 rows · Geometry: Point — the port location · PMTiles tileset
One row per port or terminal that at least one shadow-fleet vessel is recorded as having called at (~1,240 locations), geocoded to a point from GUR's embedded map. Each port carries its country, a flag for whether it lies in Russia (is_russian), and the count and IMO list of the vessels that visited. Also published as a PMTiles tileset for web mapping.
Use vessel_count to find the busiest hubs, or is_russian to isolate loading terminals inside Russia. Join port_id to the visits and vessels tables to expand the vessel lists.
🧭 19,678 rows · Geometry: Point — the visited port location · PMTiles tileset
The many-to-many link between vessels and ports — one row per recorded port call (~19,700 edges). Each row carries the vessel (IMO, GUR id, name), the port (id, name, country, Russian flag) and the port's point geometry, so it maps directly. Join to vessels on imo and to ports on port_id. Also published as PMTiles.
This is the table to query for movement patterns: which vessels touched a given port, how many calls a vessel made, or the share of calls landing at Russian terminals (is_russian).
One row per company (~1,500) that appears as a ship owner, commercial manager or safety manager of a shadow-fleet vessel — typically the single-vessel shell structures in jurisdictions like Hong Kong and the Seychelles that obscure beneficial ownership. Carries the company IMO id, name, country, the role(s) it plays and the vessels it is tied to. Non-spatial plain Parquet.
Join company_imo back to the nested ship_owner / commercial_manager / safety_manager structs in vessels. roles is a JSON array because a company can play more than one role across the fleet.
One row per ship master (captain) GUR names in connection with a shadow-fleet vessel (534 individuals), with name, date of birth, tax id (TIN), citizenship and the vessel they command (name, GUR id, IMO). This is personal data published by GUR — handle it accordingly. Non-spatial plain Parquet; join to vessels on vessel_imo / vessel_gur_id.
Grouping by citizenship shows which nationalities crew the fleet; joining to vessels attaches each master to the tanker's sanctions and ownership context.
Every table is a single Parquet file you can query directly over HTTP with DuckDB — no download step. The spatial tables (vessels, ports, visits) are GeoParquet; ports and visits also ship as PMTiles for web maps.
See the per-collection llms.txt files for join keys and more examples, or open the Portolan browser to explore interactively.