Sink
End-to-end encrypted messages relayed phone to phone with no server, account or phone number.
- Role
- Design, protocol, engine, app and CI
- Tests
- 43 automated engine tests, run in CI
- Code
- 111 Kotlin files across engine, core and feature modules
- Source
- Public
- Stack
- Kotlin · Jetpack Compose · Room · Hilt · Nearby Connections · WorkManager · Android Keystore · JCA crypto · Gradle · GitHub Actions
Problem
When connectivity fails, messaging usually fails with it. Sink asks how far people can still talk using only the phones around them, and what that costs in security and reliability.
What I built
- A routing engine using TTL- and hop-bounded flooding, duplicate suppression, ACK propagation and store-and-forward retry with exponential backoff.
- A signed, versioned binary wire protocol with a self-certifying HELLO identity handshake.
- End-to-end encryption using ephemeral ECDH, HKDF-SHA256 and AES-256-GCM, with ECDSA signatures on packets.
- A hardware-backed device identity: a non-exportable Keystore signing key, with StrongBox where available.
- Nearby Connections and SMS transports, a foreground service, and a settings screen where each toggle is functionally wired.
- A real mesh view and honest connectivity states. It shows direct connections only and never fabricates a topology.
Architecture
Repository layout
engine/ pure Kotlin/JVM, no Android dependency
protocol/ packet format, ids, codecs
crypto-core/ ECDH · ECDSA · AES-GCM · HKDF (JCA)
mesh-engine/ RoutingEngine, TransportManager,
mesh simulator + tests
app/ Compose entry point, DI wiring
core/ common · crypto · database · datastore
networking · logging · permissions
feature/ onboarding · home · conversations · chat
contacts · discovery · mesh · settings The engine builds and tests anywhere with a JDK, without the Android SDK. The Android project includes it as a composite build.
Engineering decisions
Flooding over topology-aware routing
Neighbours change as people walk and phones sleep, so keeping a current topology graph is costly. Bounded flooding with a dedup cache degrades gracefully: a lost relay removes one path, not a routing table. This is recorded as a deliberate MVP-scope decision.
Keep the engine free of Android
Routing, crypto and protocol code can be unit-tested on the JVM and exercised by a simulator that stands in for real radios.
Security documents ship inside the app
The security and threat-model documents are bundled as assets and readable from the About screen, so users see the limits as well as the claims.
Block enforcement at the routing layer
Blocked contacts have their keys withheld in the routing layer, not only hidden in the interface.
Current state and limits
- Traffic analysis is not defended against: packet size, timing and sender/destination ids are visible to relays. This is disclosed in the threat model.
- First contact relies on trust-on-first-use unless the user verifies a safety number.
- Published builds are debug-signed. A versioned signed release awaits release-signing setup.
- Throughput and battery behaviour in large real-world meshes have not been measured.