Inspiration
This journey began in November–December of last year, right after I shut down my ride-sharing platform. While exploring decentralized technologies and how they could be applied to real-world problems, I stumbled upon Pears Runtime — and that discovery sparked something big.
At the same time, I was working a regular minimum-wage job (I’m not allowed to mention the company), but it involved handling queries related to a feature I’d never seen before — something designed for families and kids, and honestly, it was awesome.
That’s when the idea started to take shape.
What it does
The app is a real-time location-sharing tool designed with privacy at its core.
- 🛰️ No signups
- 🔒 No data collection
- 🚫 No middlemen
Just simple, secure, decentralized location sharing — perfect for families, friends, or anyone who values privacy.
How we built it
Let’s just say… I had to spend a lot of time learning — and unlearning — things. 😅
It all started with deep-diving into decentralized peer-to-peer (P2P) networking — specifically DHT, Hyperswarm, and Hypercore. I wanted to understand how networking could work without servers. Techniques like UDP hole punching (to establish direct connections between peers) fascinated me, especially how they could eliminate middlemen like traditional STUN/TURN servers in WebRTC.
Once I got the P2P layer working, the next challenge was integrating it into mobile apps. That’s where Bare Runtime came in — a lightweight JavaScript runtime that allowed me to use Hypercore modules natively in Swift and Kotlin apps. I had to implement an IPC (inter-process communication) layer to enable communication between the native and JS layers, using streams as the bridge.
Then came the maps. I dove into how traditional map systems work — how tiles are generated, how XYZ coordinates are used, and how services like OSM serve PMTiles/MBTiles. I started by experimenting on the web, then downloaded the full 128GB OSM dataset to convert it into a Hypercore-based format. This turned the tiles into an append-only log, which meant every peer could serve and fetch tiles — making the system fully decentralized and instantly scalable, because every device is both a client and a server.
On the mobile side, integrating MapLibre Native was a major hurdle. I used hypercore-blobs to locally serve tiles on iOS and Android, enabling smooth offline map rendering — all without relying on centralized servers.
Finally, I added location tracking and real-time sharing over the P2P network. With that, the core experience was complete.
Of course, there's still a long way to go — but the foundation is solid, and it’s fully decentralized from the ground up. 💪
Challenges we ran into
Building something truly decentralized — especially on mobile — came with plenty of tough (and sometimes weird 😅) challenges. Here are a few that stood out:
1. 🧠 Integration of Bare Runtime
I started with a Swift-based iOS app, so the first major hurdle was figuring out how to communicate between Swift and the Bare Runtime (which runs JavaScript). Since IPC (inter-process communication) in Bare is synchronous by default, I had to dig deep into Swift’s async/await, AsyncSequence, and stream handling to build a bridge between the native and JS layers.
Once the Swift side was up and running, the Kotlin side seemed like it would be easier. But I ran into a strange bug: the data was arriving out of order — and after hours of debugging, I realized I had completely overlooked UTF-8 encoding 🤦. Once that was fixed, everything started working properly.
2. 🗺️ Displaying the Map with MapLibre Native
Rendering maps turned out to be one of the trickiest parts.
Since I only needed to load specific PMTiles/MBTiles based on x/y/z coordinates, I had to build a system that could serve tiles on demand. Thankfully, PMTiles supports HTTP range requests, and so does hypercore-blob-server — so that part clicked instantly 🔥.
However, hypercore-blob-server was generating a new token for every request (for security), which made it impossible to hardcode the tile server URL into style.json. I submitted a merge request to add a flag to disable token generation, so I could embed a static local URL in the map config. ✅
Even then, the map still wouldn't show. After digging deeper, I discovered that both iOS and Android block HTTP cleartext traffic by default. Once I updated the permissions to allow local HTTP connections, everything came to life — and it was honestly magical to finally see it work. ✨
While MapLibre Native doesn’t officially support PMTiles in production yet, I got it working well enough for our use case.
3. 📍 Annotations on MapLibre Native
This one was brutal. 😩
I spent over two weeks trying to get simple annotations (like pins and markers) working on MapLibre Native. The documentation wasn’t super helpful, and to make things worse, annotations just didn’t work properly when using Jetpack Compose or SwiftUI.
After tons of trial and error, I finally found a solution: falling back to old-school UI frameworks — UIKit on iOS and XML layouts on Android. Not ideal, but it worked. 😅
4. 🔁 Integrating Bare Runtime in Kotlin Multiplatform (KMP)
Initially, I had two separate native codebases — so getting started with Kotlin Multiplatform (KMP) seemed like a natural step.
But of course, it wasn’t that simple. 😅
I had to figure out how to use Bare Runtime within the KMP setup. I reused the same strategy I used with the standalone codebases, and surprisingly, it worked. I did spend time exploring CInterop for deeper integration, but ran into roadblocks — especially because direct communication between Swift and Kotlin isn’t natively supported.
5. 🧩 Building the IPC Layer Across Platforms
To bridge Swift ↔ Kotlin, I looked into examples by John O'Reilly and tried libraries like SKIE, which worked really well — except for one critical issue: asynchronous reading from Swift to Kotlin wasn’t possible in my use case.
I had to find a workaround — a bit hacky, but effective. I used expect/actual declarations in KMP to build a custom read handler on both platforms. The rest of the IPC logic lived in shared KMP code, and it worked beautifully.
Despite being completely new to KMP, I’ve come to really appreciate how flexible and powerful it is. Definitely something I’ll keep exploring. 🚀
Each of these challenges pushed me to learn things I’d never touched before — from low-level networking to advanced mobile IPC and KMP internals. The process wasn’t always fun, but seeing it all come together made it 100% worth it. 🙌
Each of these challenges taught me something new — and honestly, overcoming them made the end result feel that much more rewarding. Still plenty of quirks and optimizations left, but the foundation is solid.
Accomplishments that we're proud of
🚀 Built a fully peer-to-peer real-time location sharing app — no servers, no signups, no data collection, and zero infrastructure cost.
🌍 Designed for scalability from day one — thanks to a decentralized architecture, every user acts as both client and server, making the system naturally scalable and resilient.
🔒 Privacy-first by design — no accounts, no tracking, no third-party analytics — users keep complete control of their data.
🧱 Integrated Bare Runtime with Swift & Kotlin — enabling native mobile apps to run JavaScript modules directly, with custom-built IPC bridges.
📡 Built custom P2P communication using Hypercore, Hyperswarm, and DHT — fully serverless and direct peer connections using UDP hole punching.
What we learned
This project was a crash course in decentralized systems, mobile internals, and cross-platform architecture. Some of the key takeaways:
🌐 Decentralization is powerful — but not easy
Truly serverless and peer-to-peer systems require rethinking everything from networking to data delivery. It's not just “replace the server” — it’s design, distribute, and trust the network itself.🔄 IPC across native and JS runtimes is a beast
Integrating Bare Runtime into Swift, Kotlin, and Kotlin Multiplatform (KMP) taught us a lot about streams, async communication, and low-level process coordination. Understanding and bridging different runtimes is now second nature.🧭 Mobile platform quirks can break everything
Things like HTTP cleartext restrictions, permission configurations, or layout system differences between Jetpack Compose vs XML, or SwiftUI vs UIKit, can make or break features — even when your logic is solid.🗺️ Modern map rendering is a world of its own
From tile servers to PMTiles and range requests, to rendering using MapLibre Native — building a fully offline and decentralized map system was one of the most complex things, but also the most satisfying.🧪 Sometimes the old way is still the best way
While newer UI frameworks are amazing, sometimes falling back to XML or UIKit solves the problem faster. Pragmatism > idealism.🧠 Problem-solving beats "knowing it all"
There were many points where we had no idea how to proceed — but persistence, experimentation, and digging into docs/code/examples led to breakthroughs every time.
This wasn’t just about building an app — it was about pushing ourselves into unfamiliar territory, solving real-world decentralized problems, and coming out more confident in our ability to learn, adapt, and build.
What's next for WhereFam
- Places alerts
- Location History
- Local Roadside Assitance
- SOS alerts / Emergency services
Built With
- bare
- cmp
- dht
- hypercore
- hyperswarm
- javascript
- kmp
- kotlin
- sqlite
- swift
- swiftui

Log in or sign up for Devpost to join the conversation.