Track vessels in real-time using live AIS data on an interactive map.
![]() |
![]() |
|---|---|
![]() |
![]() |
- 🗺️ Real-time Tracking - live AIS streams
- 🤖 AI Enhance - Support LLM provißder to enhance AI analysis
- 🌍 Interactive Map - Leaflet-based visualization with vessel markers and tracks
- 🔍 Search & Filter - Find vessels by name or MMSI, filter by type and location
- 📊 Historical Tracks - View vessel movement history
- 🔄 Live Updates - WebSocket-powered updates without page refresh
- � Time-Series Storage - TimescaleDB for efficient historical queries
- ⚡ Fast Caching - Redis for quick access to recent positions
# Clone the repository
git clone <repository-url>
cd smart-ais-mvp
# Set up environment
cp .env.example .env
# Edit .env and add your AISStream API key
# Install dependencies
npm run install:all
# Start backend
cd backend && npm run dev
# Start frontend (in another terminal)
cd frontend && npm run devGet your free API key at https://aisstream.io
Access the app at http://localhost:5173
smart-ais-mvp/
├── backend/ # Node.js/Express backend service
│ ├── src/
│ │ ├── api/ # REST API routes and middleware
│ │ ├── db/ # Database connection
│ │ ├── repositories/ # Data access layer
│ │ ├── services/ # Business logic (AISStream, Cache, Pipeline, WebSocket)
│ │ ├── types/ # TypeScript type definitions
│ │ ├── utils/ # Utilities (logger, errors)
│ │ └── server.ts # Application entry point
│ └── package.json
├── frontend/ # React frontend application
│ ├── src/
│ │ ├── components/ # React components (Map, VesselList, Search, etc.)
│ │ ├── hooks/ # Custom React hooks (useVesselTracking)
│ │ ├── utils/ # Utilities (GeoJSON conversion)
│ │ ├── config/ # Configuration
│ │ └── types/ # TypeScript type definitions
│ └── package.json
├── database/
│ └── init/ # Database initialization scripts
│ ├── 01-create-schema.sql
│ └── 02-create-views.sql
├── .env.example # Environment variables template
└── package.json # Root package.json for monorepo scripts
- Node.js 18+ and npm
- PostgreSQL 14+ with TimescaleDB extension
- Redis 7+
- AISStream API key (free at aisstream.io)
-
Clone the repository
git clone <repository-url> cd smart-ais-mvp
-
Install PostgreSQL with TimescaleDB
macOS:
brew install timescaledb createdb ais_db psql ais_db -c "CREATE EXTENSION IF NOT EXISTS timescaledb;"Ubuntu/Debian:
sudo apt-get install postgresql-14 postgresql-14-timescaledb createdb ais_db psql ais_db -c "CREATE EXTENSION IF NOT EXISTS timescaledb;" -
Install and start Redis
macOS:
brew install redis && brew services start redisUbuntu/Debian:
sudo apt-get install redis-server && sudo systemctl start redis -
Set up backend
cd backend npm install cp .env.example .envEdit
backend/.envwith your settings:AISSTREAM_API_KEY=your_api_key_here DATABASE_URL=postgresql://ais_user:ais_password@localhost:5432/ais_db REDIS_URL=redis://localhost:6379
Initialize database and start:
psql ais_db < ../database/init/01-create-schema.sql psql ais_db < ../database/init/02-create-views.sql npm run dev
-
Set up frontend
cd frontend npm install npm run devEdit 'frontend/.env' with your settings:
# AI/LLM Configuration VITE_LLM_API_BASE_URL= VITE_LLM_MODEL= VITE_LLM_API_KEY=
6. **Open the app**
- Frontend: [http://localhost:5173](http://localhost:5173)
- Backend API: [http://localhost:3000](http://localhost:3000)
## Environment Variables
### Backend `backend/.env`
```bash
# Required
AISSTREAM_API_KEY=your_api_key_here
DATABASE_URL=postgresql://ais_user:ais_password@localhost:5432/ais_db
REDIS_URL=redis://localhost:6379
# Optional
NODE_ENV=development
PORT=3000
CORS_ORIGIN=http://localhost:5173
LOG_LEVEL=info
# Defaults work for local development
VITE_API_URL=http://localhost:3000
VITE_WS_URL=ws://localhost:3000Base URL: http://localhost:3000/api
List vessels with optional filters.
Query Parameters:
mmsi,name,type- Filter by vessel attributesminLat,maxLat,minLon,maxLon- Bounding boxspeedMin,speedMax- Speed range in knotslimit,offset- Pagination
Example:
curl "http://localhost:3000/api/vessels?type=70&limit=10"Response:
{
"vessels": [
{
"mmsi": "367719770",
"name": "OCEAN EXPLORER",
"vessel_type": 70,
"latitude": 37.7749,
"longitude": -122.4194,
"sog": 12.5,
"cog": 285.0,
"timestamp": "2025-12-01T10:30:00Z"
}
],
"count": 10,
"timestamp": "2025-12-01T10:30:15Z"
}Get vessel details by MMSI.
Example:
curl "http://localhost:3000/api/vessels/367719770"Response:
{
"vessel": {
"mmsi": "367719770",
"name": "OCEAN EXPLORER",
"vessel_type": 70,
"imo_number": 1234567,
"call_sign": "WDD1234",
"dimension_a": 50,
"dimension_b": 50,
"dimension_c": 10,
"dimension_d": 10
},
"position": {
"latitude": 37.7749,
"longitude": -122.4194,
"sog": 12.5,
"cog": 285.0,
"timestamp": "2025-12-01T10:30:00Z"
},
"timestamp": "2025-12-01T10:30:15Z"
}Get vessel position history.
Query Parameters:
startTime,endTime- ISO 8601 timestamps (defaults to last 24 hours)
Example:
curl "http://localhost:3000/api/vessels/367719770/track?startTime=2025-12-01T00:00:00Z"Response:
{
"mmsi": "367719770",
"startTime": "2025-12-01T00:00:00Z",
"endTime": "2025-12-01T10:30:00Z",
"track": [
{
"timestamp": "2025-12-01T00:00:00Z",
"latitude": 37.7700,
"longitude": -122.4200,
"sog": 10.0,
"cog": 280.0
}
],
"count": 150,
"timestamp": "2025-12-01T10:30:15Z"
}Search vessels by name or MMSI.
Query Parameters:
q- Search query (required)limit- Max results
Example:
curl "http://localhost:3000/api/search?q=OCEAN&limit=10"Response:
{
"query": "OCEAN",
"results": [
{
"mmsi": "367719770",
"name": "OCEAN EXPLORER",
"vessel_type": 70,
"latitude": 37.7749,
"longitude": -122.4194
}
],
"count": 10,
"timestamp": "2025-12-01T10:30:15Z"
}Health check endpoint.
Example:
curl "http://localhost:3000/api/health"Response:
{
"status": "healthy",
"timestamp": "2025-12-01T10:30:15Z",
"services": {
"database": "connected",
"redis": "connected"
}
}Connect to ws://localhost:3000
Events:
vesselUpdate- Real-time position updatesconnect/disconnect- Connection status
Example:
import io from 'socket.io-client';
const socket = io('http://localhost:3000');
socket.on('connect', () => {
console.log('Connected to WebSocket server');
});
socket.on('vesselUpdate', (data) => {
console.log('Vessel update:', data);
// data contains: { mmsi, latitude, longitude, sog, cog, timestamp }
});Errors return a JSON object with code, message, and timestamp.
npm run install:all # Install dependencies
npm run dev:backend # Start backend
npm run dev:frontend # Start frontend
npm run build:all # Build both
npm run test:all # Run all tests
npm run lint:all # Lint codenpm run dev # Start dev server
npm run build # Build for production
npm test # Run tests
npm run lint # Lint codeRun tests from root or individual directories:
npm run test:all # All tests
cd backend && npm test # Backend only
cd frontend && npm test # Frontend only
npm test -- --coverage # With coverage┌─────────────────────────────────────────────────────────────┐
│ Frontend (React) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Map Component│ │ Vessel List │ │ Search/Filter│ │
│ │ (Leaflet) │ │ Component │ │ Component │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ↕ ↕ ↕ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ WebSocket Client (Socket.io) │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕ WebSocket
┌─────────────────────────────────────────────────────────────┐
│ Backend (Node.js/Express) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ WebSocket │ │ REST API │ │ AISStream │ │
│ │ Server │ │ Endpoints │ │ Manager │ │
│ │ (Socket.io) │ │ │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ ↕ ↕ ↕ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Data Pipeline & Business Logic │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↕
┌─────────────────────────────────────────────────────────────┐
│ Data Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ PostgreSQL │ │ Redis Cache │ │ AISStream │ │
│ │ (TimescaleDB)│ │ │ │ WebSocket │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
Frontend: React 18, TypeScript, Leaflet, Socket.io, TailwindCSS, Vite
Backend: Node.js 18+, Express, Socket.io, TypeScript, Jest
Data: PostgreSQL with TimescaleDB, Redis
- AISStreamManager - Manages WebSocket connection to AISStream API
- DataPipeline - Processes and stores incoming AIS messages
- VesselRepository - Data access layer
- CacheService - Redis caching for recent data
- WebSocketServer - Broadcasts updates to clients
Cannot connect to AISStream:
- Check your API key in
.env - Verify internet connection
Database connection errors:
- Make sure PostgreSQL is running
- Verify DATABASE_URL in
.env
Redis connection errors:
- Make sure Redis is running
- Check REDIS_URL in
.env
Frontend can't reach backend:
- Check backend health:
curl http://localhost:3000/api/health - Verify VITE_API_URL in
frontend/.env
Port already in use:
lsof -ti:3000 | xargs kill -9 # Backend
lsof -ti:5173 | xargs kill -9 # Frontend- Kiro for vibe-coding
- AISStream for real-time AIS data
- Leaflet for map visualization
- TimescaleDB for time-series storage
- OpenMeteo for weather data



