A real-time maritime fleet tracking and command system for the Strait of Hormuz. Features live ship simulation, AI-powered distress analysis, weather-aware routing, and role-based operations for Command and Captain roles.
- Node.js 20+
- npm
- Supabase project (for persistence)
- Clerk account (for authentication)
- Groq API key (for AI analysis)
cd backend
npm install
node index.jsExpected output:
🚀 Backend listening on http://localhost:4000
🔌 WebSocket at ws://localhost:4000/ws
💚 Health check at http://localhost:4000/health
🚢 Simulator started — 15 ships, tick 1000ms
🌊 Weather service started (5-min refresh)
cd frontend
npm install
npm run devOpens on http://localhost:3000
- Open http://localhost:3000 → Sign in with Clerk
- Navigate to
/commandfor Command dashboard - Navigate to
/captainfor Captain view
PORT=4000
FRONTEND_URL=http://localhost:3000
CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=
GROQ_API_KEY=
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
NEXT_PUBLIC_BACKEND_URL=http://localhost:4000
NEXT_PUBLIC_WS_URL=ws://localhost:4000/ws
Run the following in the Supabase SQL Editor:
create table ship_snapshots (
id uuid default gen_random_uuid() primary key,
snapshot_time timestamptz default now(),
ships_data jsonb not null,
created_at timestamptz default now()
);
create table alerts (
id uuid default gen_random_uuid() primary key,
type text not null,
ship_id text not null,
message text,
severity text default 'medium',
acknowledged boolean default false,
created_at timestamptz default now()
);
create table directives (
id uuid default gen_random_uuid() primary key,
ship_id text not null,
issued_by text,
type text not null,
payload jsonb,
status text default 'pending',
created_at timestamptz default now()
);
create table distress_messages (
id uuid default gen_random_uuid() primary key,
ship_id text not null,
raw_message text,
ai_analysis jsonb,
created_at timestamptz default now()
);| Layer | Technology |
|---|---|
| Frontend | Next.js 16 (App Router), React, TypeScript, Tailwind CSS |
| Map | Leaflet + react-leaflet |
| Auth | Clerk (RBAC: command / captain) |
| Backend | Node.js, Express, WebSocket (ws library) |
| Database | Supabase (PostgreSQL) |
| AI | Groq API (LLaMA 3.3 70B) |
| Weather | Open-Meteo (free, no API key) |
┌─────────────┐ WebSocket ┌─────────────────┐
│ Frontend │◄──────────────────►│ Backend │
│ Next.js │ (STATE_UPDATE, │ Express + WS │
│ Port 3000 │ ALERT, etc.) │ Port 4000 │
└─────────────┘ └──────┬──────────┘
│
┌──────┴──────┐
│ Simulator │
│ Engine │
│ (1Hz tick) │
└──────┬──────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
Supabase Open-Meteo Groq AI
(persist) (weather) (NLP)
- 15 ships simulating in the Strait of Hormuz with realistic positions and routes
- A* grid-based pathfinding constrained to navigable water polygon
- 1Hz simulation tick with position updates, fuel depletion, and status management
- WebSocket real-time sync (< 500ms latency, 5+ simultaneous clients)
- 60fps interpolated ship movement on map (requestAnimationFrame)
- normal — ship en route to destination
- rerouting — recalculating path around zones
- distressed — captain escalated emergency
- stopped — manually held or out of fuel
- stranded — no valid route to destination
- arrived — reached destination port
- insufficient_fuel — predicted to run out before arrival (continues moving)
- out_of_fuel — fuel depleted (stops moving)
- Real-time weather from Open-Meteo (wind speed + wave height)
- 5×5 grid overlay on map showing adverse conditions
- Adverse threshold: wind > 25 kn OR wave > 2.5m
- 30% fuel burn penalty in adverse weather cells
- Storm icon indicator on affected ships
- Refreshes every 5 minutes with fallback data
- Geofence breach — ship enters restricted zone (< 1 second detection)
- Proximity warning — ships within 2 km (hysteresis clears at 2.5 km)
- Fuel prediction — alerts when ship can't reach destination with remaining fuel
- Zone prediction — alerts when ship will enter restricted zone within 5 minutes
- Stranded — no valid path exists
- Priority scoring: base type score × severity multiplier + urgency bonuses
- Audio alert (440Hz Web Audio tone) on new alerts
POST /api/ai/analyze-distress— structured distress message analysisPOST /api/ai/fleet-advisor— proactive fleet management suggestions- AI analysis includes: severity, category, injury count, damage estimate, recommended action
- Results saved to Supabase and displayed in real-time
- Command — full fleet overview, draw zones, issue directives, AI advisor
- Captain — own ship status, directives inbox, distress composer
- Roles from Clerk
publicMetadata.role("command" | "captain")
- 60-minute history at 30-second snapshot resolution
- Timeline scrubber with play/pause and speed controls (1×/2×/5×)
- Event dots on timeline for alert visualization
- "PLAYBACK MODE" banner when scrubbing, "Live" button to return
| Method | Path | Description |
|---|---|---|
| GET | /health |
Server status, ship count, client count |
| GET | /api/ships |
Current state of all 15 ships |
| GET | /api/alerts |
All alerts (prioritized) |
| GET | /api/directives |
All directives from Supabase |
| GET | /api/weather |
Current weather grid and adverse cells |
| GET | /api/history |
Last 60 min snapshots at 30s resolution |
| POST | /api/ai/analyze-distress |
AI distress message analysis |
| POST | /api/ai/fleet-advisor |
AI fleet management suggestions |
| Direction | Type | Description |
|---|---|---|
| Server→Client | INIT_STATE |
Full fleet state on connect |
| Server→Client | INIT_ALERTS |
Unacknowledged alerts on connect |
| Server→Client | STATE_UPDATE |
Periodic ship positions + weather |
| Server→Client | ALERT |
New alert fired |
| Server→Client | ALERT_ACKNOWLEDGED |
Alert was acknowledged |
| Server→Client | DIRECTIVE_ISSUED |
New directive issued |
| Server→Client | CAPTAIN_RESPONDED |
Captain response to directive |
| Server→Client | ZONE_ADDED |
New restricted zone |
| Server→Client | ZONE_REMOVED |
Zone removed |
| Client→Server | ACKNOWLEDGE_ALERT |
Acknowledge an alert |
| Client→Server | ISSUE_DIRECTIVE |
Issue directive to ship |
| Client→Server | ADD_ZONE |
Create restricted zone |
| Client→Server | REMOVE_ZONE |
Remove restricted zone |
| Client→Server | CAPTAIN_RESPONSE |
ACCEPT or ESCALATE_DISTRESS |
1. Docker / docker compose — not implemented. Run backend and frontend manually (see above).
2. Railway deploy — not implemented. System runs locally only.
3. Real coastline data — not used. A simplified navigable water polygon is used as specified.
4. Multiple route options (bonus) — not implemented.
5. Ship-to-ship assistance (bonus) — not implemented.
6. AI fleet advisor proactive suggestions (bonus) — partially implemented via /api/ai/fleet-advisor endpoint.
7. Predictive alerts (bonus) — implemented via 5-minute projection in tick loop.
| Feature | Status | Details |
|---|---|---|
| Predictive zone alerts | ✅ Full | 5-minute forward projection, fires alert with ETA |
| Predictive fuel alerts | ✅ Full | Distance-short calculation, severity-based |
| Proximity hysteresis | ✅ Full | Warn at < 2km, clear at > 2.5km, no flapping |
| Weather integration | ✅ Full | Open-Meteo real data, 5×5 grid, fuel penalty |
| AI distress analysis | ✅ Full | Groq LLaMA 3.3 70B, structured JSON output |
| AI fleet advisor | ✅ Partial | Endpoint works, UI panel for accept/dismiss |
| Playback timeline | ✅ Full | 60min history, scrub, speed controls, event dots |
| Audio alerts | ✅ Full | Web Audio API, 440Hz tone on new alerts |