# How BitChat Implements Geohash-Based Location Channels for Privacy-Preserving Local Communication

> Discover how BitChat uses geohash-based location channels for private local communication, mapping GPS data to hierarchical rooms without revealing exact positions.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: internals
- Published: 2026-08-04

---

**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)](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/GeoRelayDirectory.swift). This lightweight value object encapsulates two critical properties:

- `level` — a `GeohashChannelLevel` enum value determining spatial granularity
- `geohash` — 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.

```swift
// 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)](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`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/GeohashPresenceService.swift) manages this protocol:

1. Listens for incoming presence events from peers
2. Aggregates active users per `GeohashChannel`
3. Publishes consolidated presence counts to UI subscribers
4. Throttles outbound broadcasts via internal scheduling to prevent network flooding

```swift
// 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`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/GeohashChatActivityTracker.swift) 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`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Nostr/GeoRelayDirectory.swift), 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)](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.