>_The Problem
Natural disasters, infrastructure failures, and conflict zones share a common trait: traditional communication fails first. Cell towers go offline, internet links saturate, centralized services become unreachable.
Existing alternatives either require specialized hardware (LoRa, satellite phones), depend on cloud infrastructure (Signal, WhatsApp), or sacrifice privacy for connectivity. There was no phone-to-phone, zero-infrastructure, encrypted messaging solution that works on standard consumer devices.
Mesh fills that gap. Built during the EU Critical Infrastructure Hackathon (where it received an Honorable Mention), it turns every smartphone into a mesh relay node — no SIM, no Wi-Fi, no servers required.
>_Architecture
Turborepo monorepo with 3 packages and 2 apps. Each layer has a single responsibility — protocol logic never touches native Bluetooth, and native modules never touch UI.
mesh-core
885 LOCShared protocol layer. Defines MeshMessage type with TTL routing, Ed25519 signatures, encryption provider interface, and the relay decision algorithm. Platform-agnostic TypeScript.
mesh-android
1,571 LOCKotlin native module with dual transport: WiFi Direct (ServerSocket on port 8888, P2P group negotiation) and BLE (GATT service 6E400001, central + peripheral roles, 512-byte MTU). Exposes React Native bridge via MeshAndroidModule.
mesh-ios
999 LOCSwift native module using CoreBluetooth exclusively (iOS lacks WiFi Direct). Implements CBCentralManager for scanning and CBPeripheralManager for advertising. Same GATT service UUIDs as Android for cross-platform interop.
mobile app
6,161 LOCExpo Router app with platform abstraction layer. MeshNetwork.ts bridges to native modules, SecureMessaging.ts handles encryption, MessageQueue.ts manages offline retry with exponential backoff (1s → 5m, 5 attempts max).
pwa gateway
975 LOCVite-based Progressive Web App acting as internet gateway. WebSocket relay to EU infrastructure endpoint. Persists last 100 messages in localStorage. Emergency dispatch protocol for government channels.
>_End-to-End Encryption
Every message uses ephemeral key exchange for forward secrecy. No private key ever leaves the device. No server ever sees plaintext.
Key Generation
Each device generates Ed25519 (signing) and X25519 (encryption) key pairs from 32-byte random seeds. Identity fingerprint = first 8 bytes of SHA-256(publicKey) as hex.
Ephemeral ECDH
Per-message: generate fresh X25519 ephemeral keypair. Compute shared secret via ECDH(ephemeralSecret, recipientPublicKey). Forward secrecy — compromising long-term keys can't decrypt past messages.
Symmetric Encryption
XChaCha20-Poly1305 with 24-byte random nonce. Fallback chain: XChaCha20 → ChaCha20 → AES-256-GCM. Authenticated encryption prevents tampering.
Signature
Entire unsigned message JSON is signed with sender's Ed25519 key. Recipients verify signature against known peer registry before processing.
Wire Format
[ephemeralKey | nonce | ciphertext | auth_tag] — everything needed for the recipient to decrypt without prior key exchange.
>_Mesh Routing
Store-and-forward with TTL-based flooding, two-layer deduplication, and priority-scored relay queues.
TTL-Based Flooding
Default TTL = 10 hops. Each relay decrements TTL and appends self to route array. Messages drop at TTL 0. With BLE ~100m range, theoretical reach is ~1km.
Loop Prevention
Route array stores every node that touched the message. If currentNode is already in route[], message is dropped. Combined with in-memory Set of 1,000 recent message IDs.
Persistent Dedup
message_routes SQLite table persists [messageId, route[], ttl, received_at] across app restarts. Two-layer strategy: fast in-memory check, durable DB fallback.
Priority Queue
Messages scored: +10 for direct (vs broadcast), -ageMinutes (newer = higher), +remainingTTL (less relayed = higher). Direct messages always win.
Offline Queue
MessageQueue.ts holds pending messages with exponential backoff: 1s, 5s, 30s, 1m, 5m. Immediate flush when recipient peer connects. 7-day TTL on queued messages.
>_Platform Strategy
Android and iOS have fundamentally different P2P capabilities. The architecture accounts for this asymmetry.
📶Android
- ▸WiFi Direct (primary) — 250 Mbps, ~200m range, group owner negotiation
- ▸BLE (fallback) — lower power, ~100m, simultaneous central + peripheral
- ▸Kotlin + Coroutines, StateFlow for reactive state, WifiP2pManager API
- ▸Both transports run concurrently — WiFi Direct for throughput, BLE for discovery
🍏iOS
- ▸BLE only — Apple restricts WiFi Direct API access for third-party apps
- ▸CoreBluetooth with dedicated DispatchQueue (.userInitiated QoS)
- ▸Dual role: CBCentralManager (scanner) + CBPeripheralManager (advertiser)
- ▸Same GATT service UUID as Android — cross-platform BLE interop out of the box
🌐PWA Gateway
The PWA acts as a bridge between the mesh and the internet. When a PWA node has connectivity, it can relay mesh messages to a WebSocket gateway (wss://eu-mesh-gateway.europa.eu/ws) and dispatch emergency messages to government channels. Relaying is opt-in per message via the allowExternalRelay flag — messages stay local by default.
>_Offline-First Design
No internet required at any point. Keys, contacts, messages, and routing tables live in on-device SQLite.
Message Lifecycle
Failed messages retry with exponential backoff: 1s, 5s, 30s, 1m, 5m. When a disconnected peer reconnects, their pending queue flushes immediately. Messages older than 7 days are cleaned up automatically.
>_By the Numbers
13,139
Lines of Code
692
Source Files
5
Apps & Packages
8
SQLite Tables
10
Max TTL Hops
24
Byte Nonce Size
512
BLE MTU Bytes
8888
WiFi Direct Port
>_Testing
End-to-end tests run on physical Android devices using WebdriverIO + Appium with UiAutomator2. Two devices communicate over BLE to verify: