This is a submission for the GitHub Copilot CLI Challenge
What I Built
Ocean Sentinels — A full-stack coastal safety platform (Web + Android) with an offline BLE Bluetooth mesh network for disaster scenarios.
When natural disasters knock out cell towers, coastal communities are left stranded. Ocean Sentinels solves this by turning ordinary Android phones into a Bluetooth mesh network — hazard reports hop phone-to-phone until any device finds connectivity and uploads to the server, alerting rescue teams and authorities instantly.
The Platform
- Web App — Interactive Mapbox map with real-time incident markers, role-based dashboards for 4 user types (Public, Authority, Rescue Team, Admin), WebSocket live notifications, analytics charts, and responsive design
- Android App — 21 Jetpack Compose screens built with Material3, offline-first architecture with Room DB caching, Firebase push notifications, GPS location tracking, and the BLE mesh networking layer
- Backend — FastAPI + PostgreSQL with async operations, 16+ REST endpoints, JWT authentication, role-based access control, and mesh message deduplication
The BLE Mesh Network
This is the core innovation. When there's no internet:
- A user submits a hazard report on their phone
- The report broadcasts over BLE to nearby phones (~400m range using Coded PHY S8)
- Each phone relays it forward using store-carry-forward protocol
- When ANY phone in the chain gets internet → the report uploads to the server → authorities are alerted
No special hardware needed. Just phones.
Technical specs:
- Coded PHY S8 — 8x forward error correction, extends BLE range to ~400m (vs 100m standard)
- 72-hour time-based message expiry instead of hop-count TTL — because in a disaster, a message chain might be 50+ devices long before reaching internet
- Triple-layer deduplication — relay path tracking + in-memory LRU cache (10K entries) + database-level dedup
- SHA-256 deterministic message IDs — same report from same device always generates the same ID
- Dual PHY fallback — tries Coded PHY first for range, gracefully degrades to 1M PHY for older devices
Tech Stack
| Layer | Stack |
|---|---|
| Backend | Python 3.11, FastAPI, SQLAlchemy 2.0 (async), PostgreSQL, JWT, WebSocket |
| Frontend | Vanilla JS, Mapbox GL JS, CSS3, role-based navigation |
| Android | Kotlin, Jetpack Compose, Material3, Hilt, Room, Retrofit, Mapbox SDK, Firebase |
| Mesh | BLE 5.0 Coded PHY S8, GATT server/client, SHA-256 dedup, foreground service |
| Deploy | Render (backend), Vercel (frontend) |
Demo
Video walkthrough coming soon — will be updated here!
Live Web App: [Ocean Sentinels - Web]
GitHub Repo: https://github.com/Gowshik-S/Ocean-Sentinels
Screenshots
Web Analytics :
Web Analytics :
My Experience with GitHub Copilot CLI
GitHub Copilot CLI was instrumental across every layer of Ocean Sentinels. Here's how it shaped my development:
BLE Mesh Layer — The Hardest Part
Building a Bluetooth mesh network from scratch is complex. Copilot CLI helped me:
- Design the
BleMeshManagerthat runs a GATT server and BLE scanner simultaneously - Implement the triple-layer deduplication strategy (relay path + LRU cache + DB check)
- Build the
DeviceIdentifiersystem — Android 6+ masks real MAC addresses, so Copilot suggested aSHA-256(deviceId + androidId)fingerprint approach - Debug BLE Coded PHY negotiation and fallback logic when devices don't support PHY S8
- Architect the time-based TTL system — Copilot understood why hop-count TTL fails in disaster scenarios and suggested 72-hour expiry instead
Full-Stack Development at Speed
-
Backend: Generated all FastAPI endpoints, SQLAlchemy async models, Pydantic schemas, and JWT middleware with role-based guards —
gh copilot suggestunderstood the access control patterns I needed - Frontend: Role-based navigation manager, Mapbox GL custom markers, WebSocket reconnection handlers — Copilot scaffolded the boilerplate so I focused on user experience
- Android: 21 Compose screens with Hilt dependency injection, Room database entities, Retrofit API client — it understood my clean architecture layers and generated consistent code across data/domain/presentation
Cross-Layer Intelligence
The most impressive moment: while working on mesh message dedup in the Android BleMeshManager, Copilot correctly suggested that the backend's Incident model also needed a mesh_message_id unique constraint. It understood the end-to-end flow — BLE relay → API upload → database dedup — without me explicitly describing it.
Impact on Development
What would have taken weeks of researching BLE specs, GATT protocol documentation, and Android Bluetooth stack quirks was compressed into days. Copilot CLI didn't just autocomplete — it understood context across files, layers, and even the research paper concepts I was implementing.
It generated the NetworkModule, DatabaseModule, and AppModule with proper @Provides and @Singleton annotations — all correctly wired.
📡 BLE Mesh Network — Where Copilot Truly Shined
The mesh layer was the most complex part. I had the IEEE BitChat paper as reference, but translating academic protocols into Android BLE code is a different beast.
┌─────────────────────────────────────────────────────────────────────────┐
│ DEVICE ROLES │
└─────────────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ GATEWAY │ │ RELAY │ │ NORMAL │
│ DEVICE │ │ NODE │ │ NODE │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ ✅ Internet │ │ ❌ No Internet│ │ ❌ No Internet│
│ │ │ │ │ │
│ 🔄 Relay │◄───────►│ 🔄 Forward │◄───────►│ 📤 Send │
│ 📤 Upload │ │ 📥 Receive │ │ 📥 Receive │
│ 📥 Download │ │ 🔄 Re-broadcast │ 📤 Broadcast│
└──────────────┘ └──────────────┘ └──────────────┘
* Any device can become Gateway if it gets internet
📡 BLE Mesh Protocol Stack
┌─────────────────────────────────────────────────────────────────┐
│ MESH APPLICATION LAYER │
├─────────────────────────────────────────────────────────────────┤
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐│
│ │ Incident │ │ Status │ │ Sync │ │ Ack ││
│ │ Report │ │ Update │ │ Request │ │ Message ││
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘│
└────────┼───────────────┼───────────────┼───────────────┼────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ MESH TRANSPORT LAYER │
├─────────────────────────────────────────────────────────────────┤
│ Serialization: Protocol Buffers → Byte Array │
│ Fragmentation: 469 bytes per fragment (512-byte BLE limit) │
│ Reassembly: Sequence numbers + CRC verification │
│ Encryption: ChaCha20-Poly1305 (via libsodium) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ MESH NETWORK LAYER │
├─────────────────────────────────────────────────────────────────┤
│ TTL Management: Decrement on each hop (max 8 hops) │
│ Deduplication: SHA-256 message IDs, 10K cache │
│ Flood Control: Randomized delays to prevent collisions │
│ Peer Discovery: mDNS/Zeroconf for local service discovery │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ BLE PHYSICAL LAYER │
├─────────────────────────────────────────────────────────────────┤
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ CODED PHY S=8 │ │ STANDARD PHY │ │
│ │ (Primary) │ │ (Fallback) │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ • Long range │ │ • Standard range │ │
│ │ • ~400m outdoors │ │ • ~100m outdoors │ │
│ │ • 125 kbps speed │ │ • 1 Mbps speed │ │
│ │ • High FEC │ │ • Lower latency │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ Timeslot: Each node scans 30ms, then advertises 100ms │
└─────────────────────────────────────────────────────────────────┘
GATT Server + Scanner Setup:
I used Copilot in VS Code to help write BleMeshManager.kt — it understood that I needed a BLE GATT server advertising a custom service UUID while simultaneously scanning for other devices. When I typed the GATT server callback scaffold, Copilot auto-completed the entire onConnectionStateChange, onCharacteristicReadRequest, and onCharacteristicWriteRequest handlers with proper response codes.
Device Identification Problem:
-> Android 6+ randomizes MAC addresses, so I couldn't use them to identify devices.
-> I described the problem in a comment:
// Need a persistent unique device identifier since Android 6+ masks real MAC addresses
Built for coastal communities worldwide. When the network goes down, the mesh goes up.
Built within 2 weeks. Impossible without Github CLI !


Top comments (0)