Real-Time Geospatial Group Coordination for Motorcycle Riders
SyncRide is a privacy-first web application that enables motorcycle riders to share their real-time location with their group, send emergency SOS alerts, and stay coordinated during rides.
- 📍 Real-time Location Sharing - See all riders on a live map
- 🚨 SOS Emergency Alerts - Long-press to broadcast emergency to all riders
- 📢 Quick Status Messages - "Pit stop", "Fuel needed", "Slow down" etc.
- 🗺️ Interactive Map - Leaflet-based map with rider markers
- 📱 Offline Support - Continue tracking when connection drops, sync when back online
- 🔐 Privacy-First - 30-second location TTL, device ID rotation, GDPR/DPDP compliant
- 🌙 Dark/Light Theme - Automatic theme switching
- 📊 Trip Summary - Statistics at end of ride (distance, speed, duration)
- 💾 Trip Replay - Save your route for 7 days (with consent)
| Layer | Technology |
|---|---|
| Frontend | React 19, Vite 8, Tailwind CSS, Leaflet, Zustand |
| Backend | Node.js, Express 5, Socket.IO 4 |
| Database | MongoDB (Mongoose 9) |
| Cache | Redis (ioredis 5) |
| Monorepo | pnpm workspaces, Turborepo |
syncride-monorepo/
├── apps/
│ ├── api/ # Express + Socket.IO backend
│ │ ├── src/
│ │ │ ├── models/ # MongoDB schemas
│ │ │ ├── routes/ # REST API endpoints
│ │ │ ├── services/ # Business logic
│ │ │ ├── cache/ # Redis client
│ │ │ └── db/ # MongoDB connection
│ │ └── package.json
│ │
│ └── web/ # React frontend
│ ├── src/
│ │ ├── components/ # UI components
│ │ ├── hooks/ # Custom hooks (useSocket, useGeolocation)
│ │ ├── store/ # Zustand stores
│ │ └── App.tsx # Main app
│ └── package.json
│
├── packages/
│ ├── shared-types/ # Shared TypeScript types
│ └── config/ # Environment validation schemas
│
├── docs/ # Documentation
│ └── PROJECT_DOCUMENTATION.md
│
├── package.json # Root workspace config
├── pnpm-workspace.yaml # Workspace definition
└── turbo.json # Build pipeline
- Node.js ≥ 20.19.0
- pnpm ≥ 9.0.0
- MongoDB (local or Atlas)
- Redis (local or cloud)
# Clone the repository
git clone https://github.com/yourusername/syncride.git
cd syncride
# Install dependencies
pnpm installPORT=3000
NODE_ENV=development
FRONTEND_URL=http://localhost:5173
MONGODB_URI=mongodb://localhost:27017
MONGODB_DB_NAME=syncride-dev
REDIS_URL=redis://localhost:6379VITE_API_URL=http://localhost:3000
VITE_WS_URL=http://localhost:3000
VITE_MAPBOX_TOKEN=pk.xxx # Optional, for Mapbox tiles# Start all apps in development mode
pnpm dev
# Or start individually
pnpm --filter api dev # Backend on :3000
pnpm --filter web dev # Frontend on :5173# Build all packages
pnpm build
# Start production server
pnpm --filter api start- Host opens the app and taps "Create Trip"
- Enters their display name
- Server generates a unique 6-character trip code (e.g.,
ABC123) - Host shares the code with their riding group
- Rider opens the app and taps "Join Trip"
- Enters the 6-character trip code
- Optionally enters their display name
- Joins the trip and sees all riders on the map
- GPS positions broadcast every 3-5 seconds via WebSocket
- Positions cached in Redis (30-second TTL) for late joiners
- Map updates instantly when positions change
- Long-press (3 seconds) the SOS button to send emergency alert
- All riders receive full-screen notification with:
- Sender's name
- Distance from your location
- Option to view on map
- Alerts can be cancelled by the sender
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/trips |
Create a new trip |
| GET | /api/trips/:code |
Get trip details |
| POST | /api/trips/:code/join |
Join an existing trip |
| POST | /api/trips/:code/end |
End trip (host only) |
| POST | /api/trips/:code/leave |
Leave trip |
| GET | /api/trips/:code/summary |
Get trip statistics |
| GET | /api/privacy/export/:deviceId |
Export user data |
| DELETE | /api/privacy/delete/:deviceId |
Delete user data |
| Event | Direction | Description |
|---|---|---|
trip:join |
Client → Server | Join trip room |
location:update |
Client → Server | Send GPS position |
location:broadcast |
Server → Client | Receive rider position |
sos:send |
Client → Server | Send SOS alert |
sos:broadcast |
Server → Client | Receive SOS alert |
trip:end |
Client → Server | End the trip |
trip:ended |
Server → Client | Trip was ended |
See PROJECT_DOCUMENTATION.md for complete API documentation.
| Data Type | Retention |
|---|---|
| Live locations | 30 seconds (Redis TTL) |
| Trip metadata | 7 days (MongoDB TTL) |
| Trip replays | 7 days (if consented) |
| Consent logs | 7 years (regulatory) |
- No account required - Anonymous device-based identity
- Device ID rotation - New ID after each trip
- Explicit consent - Users choose to save trip replays
- Data export - Download all your data (JSON)
- Data deletion - Delete all your data anytime
- Location TTL - Positions auto-delete after 30 seconds
pnpm dev # Start all apps in dev mode
pnpm build # Build all packages
pnpm lint # Lint all packages
pnpm test # Run tests# Add to specific app
pnpm --filter api add express
pnpm --filter web add react-leaflet
# Add to root (dev dependency)
pnpm add -D -w typescriptTypes are defined in packages/shared-types and used by both frontend and backend:
import { Trip, Rider, Coordinates } from 'shared-types'- Built-in reconnection with exponential backoff
- Room-based broadcasting (one room per trip)
- Automatic transport fallback (WebSocket → polling)
- Named events for type-safe protocol
- Sub-millisecond latency for live location reads
- Native TTL for automatic data expiration
- Solves "late joiner" problem (new riders see existing positions)
- Ephemeral data doesn't need persistence
- Flexible schema for trip/rider documents
- TTL indexes for automatic data cleanup
- Mongoose for type-safe queries
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Leaflet - Open-source map library
- Socket.IO - Real-time communication
- Tailwind CSS - Utility-first CSS
Made with ❤️ for the riding community