How BitChat Implements Geohash-Based Location Channels for Privacy-Preserving Local Communication
BitChat uses geohash-based location channels to map latitude/longitude coordinates to hierarchical chat rooms, enabling proximity-based communication without exposing exact GPS positions.
The open-source BitChat protocol (permissionlesstech/bitchat) implements a sophisticated location-aware messaging system using geohash encoding—a compact string representation of geographic coordinates. This approach allows users to discover and communicate with nearby peers through discrete channels that preserve privacy while maintaining low-latency performance.
Core Architecture: GeohashChannel and Precision Levels
At the heart of BitChat's location system is the GeohashChannel type defined in [bitchat/Nostr/GeoRelayDirectory.swift](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/GeoRelayDirectory.swift). This lightweight value object encapsulates two critical properties:
level— aGeohashChannelLevelenum value determining spatial granularitygeohash— the encoded string representing the geographic cell
The GeohashChannelLevel enum establishes fixed precision tiers, with each level corresponding to a specific character count in the geohash string. Shorter strings cover broader areas (regional scope), while longer strings narrow to neighborhood or building-level precision.
// Create a channel for a city-level geohash (precision = 5 characters)
let cityChannel = GeohashChannel(level: .city, geohash: "u4pruy")
// Select this channel through the location state manager
locationStateManager.select(.location(cityChannel))
Ephemeral Presence Broadcasting with Nostr Events
BitChat implements a privacy-preserving presence system using ephemeral Nostr events of kind 9041. These lightweight broadcasts, documented in [docs/GeohashPresenceSpec.md](https://github.com/permissionlesstech/bitchat/blob/main/docs/GeohashPresenceSpec.md), serve as heartbeat signals that advertise a client's current geohash without persistent storage.
The GeohashPresenceService manages this protocol:
- Listens for incoming presence events from peers
- Aggregates active users per
GeohashChannel - Publishes consolidated presence counts to UI subscribers
- Throttles outbound broadcasts via internal scheduling to prevent network flooding
// Send a presence heartbeat (normally automated by the service)
try NostrProtocol.createGeohashPresenceEvent(
geohash: cityChannel.geohash,
privateKey: myNostrKey
)
// Subscribe to aggregated presence updates
geohashPresenceService.presencePublisher
.sink { presence in
print("🗺️ \(cityChannel.geohash) – online: \(presence.onlineCount)")
}
.store(in: &cancellables)
Activity Tracking and Channel Lifecycle
The GeohashChatActivityTracker monitors message and reaction activity within each geohash channel. This component:
- Identifies "hot" locations for UI highlighting
- Determines when to cease presence broadcasts for inactive channels
- Optimizes battery and bandwidth consumption by suppressing unnecessary network traffic
Geographic Relay Selection for Low Latency
BitChat optimizes message routing through GeoRelayDirectory, which maps geohash strings to geographically proximate Nostr relays. This relay selection strategy:
- Minimizes round-trip latency for real-time messaging
- Distributes load across the relay network
- Reduces single-relay bottlenecks for popular locations
Privacy Guarantees and Security Design
The geohash-based architecture provides multiple privacy protections:
- Coarse precision — A 5-character geohash spans approximately 5 kilometers; users cannot be pinpointed to exact addresses
- Ephemeral events — Presence broadcasts expire quickly and leave no persistent record
- Anonymous counts — The system surfaces only aggregate online counts, never individual identities or precise coordinates
- Opt-in messaging — Identity revelation occurs only when a user voluntarily sends a message in a channel
Integration with BitChat's Channel System
Throughout the application, location channels integrate seamlessly with the unified ChannelID type used for direct messages and group chats. As demonstrated in [bitchatTests/Services/LocationStateManagerTests.swift](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Services/LocationStateManagerTests.swift), the .location(GeohashChannel) variant enables consistent handling across UI components including people lists, map views, and message threading.
Summary
- Geohash encoding converts GPS coordinates to hierarchical channel identifiers with tunable precision
- Ephemeral Nostr events (kind
9041) enable presence awareness without persistent tracking - Tiered architecture separates channel definition (
GeohashChannel), presence management (GeohashPresenceService), and activity tracking (GeohashChatActivityTracker) - Geographic relay selection optimizes performance while maintaining decentralization
- Privacy-by-design ensures coarse location granularity and anonymous participation metrics
Frequently Asked Questions
How precise are BitChat's geohash-based location channels?
Precision depends on the selected GeohashChannelLevel. A 4-character geohash covers roughly 20 kilometers, while 6 characters narrow to approximately 1.2 kilometers. BitChat's enum-defined levels ensure users select from predetermined granularities rather than arbitrary precision, preventing accidental over-sharing of location data.
Can other users see my exact location in BitChat?
No. The protocol only exposes your presence within a coarse geohash cell and only as part of an anonymous aggregate count. Exact coordinates are never transmitted; even the geohash itself is ephemeral. Other users learn your presence only if you explicitly send a message, and even then they know only your approximate geographic area.
Why does BitChat use ephemeral events instead of persistent Nostr events for presence?
Ephemeral events (kind 9041) are never stored by relays, eliminating a persistent location history that could be data-mined. This design choice prioritizes real-time awareness over archival presence data, aligning with BitChat's privacy-first approach to location-based communication.
How does BitChat prevent spam in public geohash channels?
The GeohashChatActivityTracker monitors engagement patterns and can suppress presence broadcasts for inactive sessions. Combined with Nostr's inherent proof-of-work and rate-limiting capabilities at the relay layer, this creates natural friction against automated spam while preserving open accessibility for legitimate users.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →