Real-time logistics intelligence platform. Tracks ships and aircraft across the globe, scores route risk using geopolitical, weather, and disaster signals, and surfaces everything in a Palantir-grade dark UI — with a route planner that finds the optimal cargo path between any two cities on Earth.
Built for the mission: life-saving drugs and critical cargo must reach their destination even when wars, disasters, and sanctions disrupt normal logistics.
git clone <repo-url> && cd project-voyage
make up # builds images, runs tests, starts everythingEverything runs in Docker. You don't need Python or Node installed locally.
| Service | URL |
|---|---|
| Frontend (Next.js) | http://localhost:3000 |
| API (REST + WebSocket) | http://localhost:8000 |
| API docs (OpenAPI) | http://localhost:8000/docs |
| Redpanda Console (Kafka UI) | http://localhost:8080 |
| MLflow | http://localhost:5001 |
| Grafana | http://localhost:3001 — admin / voyage |
| Prometheus | http://localhost:9090 |
make reset && make up # wipe all data and start fresh
make test # unit tests (no Docker needed) — currently 281 passing, 89% coverage
make logs-api # tail API logs
make shell-db # open psqlEnter any two cities, ports, or countries. The planner:
- Resolves every country name to its nearest major cargo port (e.g. "China" from LA → Shanghai; "USA" from Rotterdam → New York)
- Generates 3 route variants depending on cargo priority:
- HIGH (pharma/medicine): Air Express + Safest Sea + Standard Sea
- MEDIUM/LOW: Safest Sea + Fastest Sea + Cheapest Sea
- Scores each route against live geopolitical events (GDELT, HAPI), weather, earthquakes (USGS), and natural disasters (NASA EONET)
- Automatically reroutes around the Red Sea via Cape of Good Hope when conflict severity ≥ 0.6
- Routes follow real-world shipping lanes using a maritime routing graph (prebuilt from IMO/UNCTAD data) — routes are guaranteed to stay in navigable water, correctly threading through the Strait of Hormuz, Malacca, Suez, Panama, and every other major chokepoint
- Fallback corridor system with Catmull-Rom spline smoothing and antimeridian handling for Cape-of-Good-Hope reroutes
- Vessel positions via AISstream.io WebSocket — updated continuously
- Flight positions via OpenSky Network — polled every 60s
- Real-time overlay via Redis pub/sub → WebSocket
/ws/live
- 876+ geopolitical events in the DB (GDELT conflict, HAPI displacement, USGS earthquakes M4.5+, EONET cyclones/floods/wildfires)
- Each route gets a composite risk score from DB bounding-box queries against all event types
- Risk level: low / medium / high / critical
EXTERNAL DATA SOURCES
────────────────────────────────────────────────────────────────────────
AISstream.io OpenSky Network Open-Meteo GDELT Project
(vessel AIS) (flights) (weather) (geopolitical)
USGS Earthquake NASA EONET HAPI (UN) NewsAPI / ACLED
(earthquakes) (disasters) (humanitarian) (optional)
│ WebSocket / REST / polling
▼
INGESTION LAYER (Python ARQ workers + Kafka consumers)
────────────────────────────────────────────────────────────────────────
[AIS Worker] [Flight Worker] [Weather Worker] [OSINT Workers]
│ │ │ GDELT · HAPI · USGS · EONET
└───────────────┴──────────────────┴─────────────────────┐
▼
REDPANDA (Kafka-compatible)
vessel_positions · flight_positions
weather_events · osint_events
│
┌───────────────────┤
▼ ▼
DB WRITERS ARQ WORKERS
(consumers) (cron jobs)
│
▼
PostgreSQL 16 + TimescaleDB + PostGIS
─────────────────────────────────────
vessel_positions (hypertable, 7d retention)
flight_positions (hypertable, 7d retention)
weather_snapshots (hypertable, 30d retention)
geopolitical_events(hypertable, 90d retention)
│
┌───────────┴────────────┐
▼ ▼
FASTAPI REST API REDIS pub/sub
───────────────── ────────────────
/api/v1/vessels voyage:live:*
/api/v1/flights │
/api/v1/routes/plan ▼
/api/v1/routes/risk WS /ws/live
/api/v1/routes/eta
│
▼
NEXT.JS 14 FRONTEND
─────────────────────────────────────────────
deck.gl PathLayer (route arcs, vessel dots)
Catmull-Rom splines (smooth geodesic curves)
Zustand + React Query
shadcn/ui dark theme
| # | Decision | Choice | Why |
|---|---|---|---|
| 001 | Database | PostgreSQL + TimescaleDB + PostGIS | One engine: time-series + geospatial + relational |
| 002 | Message Queue | Redpanda | Kafka API, no JVM, single binary |
| 003 | Cache + Pub/Sub | Redis | WebSocket fan-out across API replicas |
| 004 | Task Queue | ARQ | asyncio-native, pairs perfectly with FastAPI |
| 005 | Package Manager | uv | 100× faster than pip, unified lockfile |
| 006 | Backend | FastAPI | Async, auto OpenAPI docs, native WebSocket |
| 007 | ML | Separate FastAPI microservice | Heavy deps isolated, independent scaling |
| 008 | Map | deck.gl + MapLibre | WebGL — renders 500k+ points at 60fps |
| 009 | Real-time | WebSocket over SSE | Bidirectional filter commands from browser |
| 010 | Frontend State | Zustand + React Query | Lightweight, no Redux boilerplate |
| 011 | UI | shadcn/ui + Tailwind | Copy-paste ownership, full dark theme |
| 012 | Containers | Docker Compose → K8s | Local free, prod portable via Helm |
| 013 | CI/CD | GitHub Actions | PR tests + image build + deploy |
| 014 | Monitoring | Prometheus + Grafana + Sentry | Metrics, dashboards, error tracking |
POST /api/v1/routes/plan
Content-Type: application/json
X-API-Key: <key>
{
"origin": "Oman",
"destination": "China",
"priority": "high", // "high" | "medium" | "low"
"cargo_weight_tons": 10.0,
"cargo_description": "Vaccines" // optional
}
Response includes:
origin_coords/destination_coords— resolved to nearest major cargo portroutes[]— 3 route objects, each with:waypoints— dense[[lon, lat], ...]Catmull-Rom spline (ready for deck.gl PathLayer)distance_km,eta_days,cost_usd,risk_score,risk_levelcarriers[]— top 3 carrier quotes sorted by pricevia[]— human-readable chokepoint labels (Suez Canal, Singapore Strait…)risk_factors[]— per-factor breakdown (conflict, weather, earthquake…)
GET /api/v1/vessels/?limit=500 Latest position per vessel
GET /api/v1/vessels/{mmsi} Single vessel
GET /api/v1/vessels/{mmsi}/track?hours=24 Position history
GET /api/v1/flights/?limit=500&airborne_only=false Latest state vectors
GET /api/v1/flights/{icao24} Single aircraft
POST /api/v1/routes/plan Route planner (see above)
GET /api/v1/routes/risk Route risk score (ML microservice proxy)
GET /api/v1/routes/eta ETA estimate (ML microservice proxy)
GET /health Liveness probe
GET /ready Readiness probe
GET /api/v1/health/freshness Data freshness (last ingest timestamp per source)
WS /ws/live Real-time vessel + flight position stream
Send to filter:
{"type": "vessel"} vessel updates only
{"type": "flight"} flight updates only
{"type": "all"} everything (default)
Vessel message: { type, mmsi, vessel_name, lat, lon, speed, heading, ts }
Flight message: { type, icao24, callsign, lat, lon, altitude, heading, velocity, ts }
| Type | Source | Update interval | Key needed |
|---|---|---|---|
| Vessel AIS | AISstream.io WebSocket | Continuous | AISSTREAM_API_KEY (optional, mocks if blank) |
| Flights | OpenSky Network REST | Every 60s | None |
| Weather | Open-Meteo | Every hour | None |
| Geopolitical events | GDELT Project | Every 15 min | None |
| Humanitarian data | UN OCHA HAPI | Every 6 hr | None |
| Earthquakes M4.5+ | USGS GeoJSON feed | Every 6 hr | None |
| Natural disasters | NASA EONET | Every 6 hr | None |
| Armed conflict | ACLED API | Every 6 hr | ACLED_API_KEY (optional) |
| News signals | NewsAPI | On demand | NEWSAPI_KEY (optional) |
cp .env.example .env| Variable | Required | Default | Description |
|---|---|---|---|
API_KEY |
✅ | changeme-... |
Protects all API endpoints via X-API-Key header |
DATABASE_URL |
auto | set by compose | asyncpg connection string |
REDIS_URL |
auto | set by compose | Redis connection string |
KAFKA_BOOTSTRAP_SERVERS |
auto | set by compose | Redpanda broker address |
AISSTREAM_API_KEY |
optional | — | Real vessel AIS data; falls back to 10 mock vessels on major shipping lanes |
ACLED_API_KEY |
optional | — | Armed conflict data (free registration at acleddata.com) |
NEWSAPI_KEY |
optional | — | News signal enrichment (100 req/day free) |
ML_SERVICE_URL |
auto | set by compose | Internal URL of the ML microservice |
- Docker Desktop 4.x+
- uv (only for running tests locally without Docker)
make install # creates backend/.venv for IDE support + local test runs
make up # starts the full stackmake test # 281 unit tests, no Docker needed (~0.6s)
make test-cov # with HTML coverage report
make check # lint + type-check + tests (same as CI)
make test-integration # requires infra running (make infra first)project-voyage/
├── backend/
│ ├── app/
│ │ ├── api/v1/endpoints/ # REST handlers: vessels, flights, routes, health
│ │ ├── core/ # config, database, redis, security, logging
│ │ ├── models/ # SQLAlchemy ORM (TimescaleDB hypertables)
│ │ ├── schemas/ # Pydantic v2 schemas (route_plan, vessel, flight)
│ │ ├── services/ # Business logic
│ │ │ └── route_planner.py # Corridor templates, Catmull-Rom smoothing,
│ │ │ # port resolution, risk scoring, carrier quotes
│ │ └── workers/ # ARQ cron jobs + startup tasks
│ ├── ingestion/
│ │ ├── ais/ # AISstream.io WebSocket vessel worker
│ │ ├── flights/ # OpenSky flight worker
│ │ ├── weather/ # Open-Meteo weather worker
│ │ ├── osint/ # GDELT · HAPI · USGS · EONET workers
│ │ └── consumers/ # Kafka → TimescaleDB writers
│ ├── migrations/ # Alembic migration files
│ └── tests/
│ ├── unit/ # 281 fast tests, no DB or network required
│ └── integration/ # Tests against live Docker services
├── frontend/
│ └── src/
│ ├── app/ # Next.js 14 App Router pages
│ ├── components/
│ │ ├── map/ # VoyageMap (deck.gl), EntityTooltip
│ │ └── panels/ # RoutePlannerPanel, VesselPanel, FlightPanel, RiskPanel
│ ├── hooks/ # useVessels, useFlights, useWebSocket
│ ├── stores/ # Zustand: mapStore, liveStore, routeStore
│ └── types/ # TypeScript interfaces (route, vessel, flight)
├── infra/
│ ├── docker/ # docker-compose.yml, Dockerfiles, entrypoints
│ ├── k8s/ # Kubernetes manifests (base + kustomize overlays)
│ └── terraform/ # AWS Fargate (demo) + EKS (prod) modules
└── .github/workflows/ # CI: lint → type-check → test → build → deploy
| Phase | Description | Status |
|---|---|---|
| 0 | Repo scaffold, Docker Compose, CI/CD, DB migrations, monitoring | ✅ Complete |
| 1 | Real data ingestion — AIS vessels, flights, weather, GDELT/HAPI/USGS/EONET | ✅ Complete |
| 2 | Route Planner — geodesic routing, risk scoring, carrier quotes, smooth map arcs | ✅ Complete |
| 3 | ML microservice — route risk model v2, ETA forecasting, anomaly detection | 🔲 Next |
| 4 | Testing hardening, performance tuning, tech debt | 🔲 Planned |
| 5 | AI intelligence layer — LLM route explanations, natural language alerts | 🔲 Planned |
# Spin up a demo on AWS ECS Fargate (~$5–15 for a session)
cd infra/terraform/aws
terraform init && terraform apply -var-file=fargate.tfvars
# Tear down after the demo
terraform destroy -var-file=fargate.tfvarsProduction uses EKS (AWS) or GKE (GCP) with Helm — same Docker images, portable manifests.
Backend: Python 3.12 · uv · FastAPI · SQLAlchemy 2 · Alembic · Pydantic v2 · ARQ · searoute
Database: PostgreSQL 16 · TimescaleDB · PostGIS
Streaming: Redpanda (Kafka API, no JVM)
Cache: Redis 7
ML: scikit-learn · XGBoost · Prophet · Polars · MLflow · ONNX
Frontend: Next.js 14 · TypeScript · Tailwind · shadcn/ui · deck.gl · Zustand · React Query
Infra: Docker Compose · Kubernetes · Helm · Terraform (AWS + GCP)
CI/CD: GitHub Actions (lint → type-check → test → build → deploy)
Monitoring: Prometheus · Grafana · Sentry · Loki


