How BitChat's Location Channels Use the bridgeGeohash Field to Connect Mesh-Only Peers
The bridgeGeohash field acts as a rendezvous TLV (type 0x06) that advertises a peer's ability to bridge BLE mesh traffic to a specific Nostr geohash cell, enabling mesh-only devices to communicate over the internet without direct Nostr connectivity.
BitChat organizes conversations into three distinct transport layers, with location channels serving as geohash-based cells that synchronize messages between local mesh networks and Nostr relays. For devices operating exclusively on BLE mesh without internet access, the bridgeGeohash field provides the critical link needed to reach the Nostr network according to the permissionlesstech/bitchat source code. This article examines how the repository implements this bridging mechanism through TLV encoding, capability advertisement, and registry management.
Understanding BitChat's Transport Architecture
The Three Transport Layers
BitChat categorizes message transport into three layers: BLE mesh for local peer-to-peer communication, Nostr relays for global internet-based messaging, and geohash-based location channels that act as intermediaries. Location channels divide the world into hierarchical cells (country, city, neighborhood, pinpoint), allowing users to subscribe to chats based on geographic proximity.
The Mesh-to-Nostr Connectivity Gap
Mesh-only peers lack internet connectivity and cannot subscribe to Nostr relays directly. When these devices need to participate in location channel conversations that also exist on Nostr, they require a bridge node—one peer in the mesh that also maintains a Nostr connection and can forward messages between the two networks.
How the bridgeGeohash Field Advertises Bridge Capabilities
The bridgeGeohash field functions as a Type-Length-Value (TLV) structure that mesh peers broadcast to indicate which Nostr geohash cell they can bridge to. This enables automatic discovery of bridge nodes without manual configuration.
Defining Bridge Capabilities in PeerCapabilities.swift
In localPackages/BitFoundation/Sources/BitFoundation/PeerCapabilities.swift (lines 23-26), the bridge capability is defined as a bit flag that peers set when they can forward mesh traffic to Nostr:
// PeerCapabilities.swift – bridge capability definition
public static let bridge = PeerCapabilities(rawValue: 1 << 7) // bridges mesh ↔︎ geohash‑cell
When a peer sets this bit, it signals to the mesh that it can act as a gateway to the geohash cell specified in its bridgeGeohash TLV.
Encoding and Decoding the TLV in BLEService.swift
The actual wire format for the bridgeGeohash field is handled in bitchat/Services/BLE/BLEService.swift. When creating announcement packets, the service appends TLV type 0x06 followed by the UTF-8 encoded geohash string (lines 2775-2782):
// BLEService.swift – encoding bridgeGeohash
if let bridgeGeohash = bridgeGeohash,
let cellData = bridgeGeohash.data(using: .utf8) {
data.append(TLVType.bridgeGeohash.rawValue) // TLV type 0x06
data.append(cellData)
}
On receipt, the decoder extracts the geohash from the same TLV type (lines 85-90):
// BLEService.swift – decoding bridgeGeohash
case .bridgeGeohash:
bridgeGeohash = String(data: value, encoding: .utf8)
Registering Advertised Geohashes in BLEPeerRegistry.swift
Once decoded, the geohash values are persisted in bitchat/Services/BLE/BLEPeerRegistry.swift (lines 134-138), which maintains a registry of each peer's advertised bridge cell:
// BLEPeerRegistry.swift – bridgeGeohash storage
.compactMap(\.bridgeGeohash)
This allows higher-level services to query which peers can bridge to specific location channels.
Bridging Mesh Traffic to Location Channels
Establishing the Bridge Connection
When a user selects a location channel in LocationChannelManager.swift, the system checks peer capabilities to find a suitable bridge. If a mesh peer has advertised the bridge capability with a matching bridgeGeohash, the manager instantiates a bridge service (lines 312-320):
// LocationChannelManager.swift – creating a bridge
if capabilities.contains(.bridge), let cell = peer.bridgeGeohash {
bridge = BridgeService(cellGeohash: cell)
}
Bidirectional Message Flow
The bridge facilitates two-way communication between transport layers:
- Outbound forwarding: Mesh-only peers send messages to the bridge node, which signs them as a regular Nostr client and publishes them to the relay for the specified geohash cell.
- Inbound injection: Nostr events received for the bridged cell are wrapped in bridge packets and injected back into the local mesh, reaching all connected peers.
This architecture allows mesh-only devices to participate in global location channel conversations without implementing full Nostr client functionality.
Practical Implementation Example
The following Swift code demonstrates how to create an announcement packet that advertises bridge capabilities, decode it on a receiving peer, and utilize the bridge for location channel communication:
// 1️⃣ Create an announce packet that advertises the bridge capability
let caps = PeerCapabilities([.meshDiagnostics, .bridge])
let bridgeGeohash = "u4pruy" // cell that will act as the bridge
let packet = AnnouncePacket(
capabilities: caps,
bridgeGeohash: bridgeGeohash)
// 2️⃣ Decode the packet on a receiving peer
guard let decoded = AnnouncePacket(data: inboundData) else { fatalError() }
print(decoded.bridgeGeohash) // → "u4pruy"
// 3️⃣ When the user selects a location channel, the manager creates the bridge
locationChannelsModel.select(.location(GeohashChannel(level: .city, geohash: "u4pruy")))
if let bridge = BridgeService.shared {
bridge.send(message: meshMessage) // forwards to Nostr cell “u4pruy”
}
Unit tests verifying this wire format are available in bitchatTests/Protocols/BridgeWireFormatTests.swift.
Summary
- The bridgeGeohash field (TLV 0x06) serves as a rendezvous mechanism that links BLE mesh networks to Nostr geohash cells.
- Peers advertise their bridging capability using the
bridgebit flag (1 << 7) inPeerCapabilities.swiftand specify their target cell via UTF-8 encoded geohash data. BLEService.swifthandles wire-format encoding and decoding of the TLV, whileBLEPeerRegistry.swiftstores advertised geohashes for service discovery.LocationChannelManager.swiftautomatically establishes bridge connections when users select location channels, enabling transparent message forwarding between mesh-only peers and the Nostr network.
Frequently Asked Questions
What is the wire format of the bridgeGeohash field?
The field uses a Type-Length-Value encoding where the type byte is 0x06, followed by the length, and the value contains the UTF-8 encoded geohash string (typically 6 characters for city-level precision). This format is implemented in BLEService.swift for both packet creation and parsing.
How do mesh-only peers discover available bridge nodes?
Mesh peers discover bridges by receiving announcement packets from peers that have set the bridge capability bit in PeerCapabilities. The BLEPeerRegistry automatically extracts and stores the bridgeGeohash value from these announcements (lines 134-138), making it available to the LocationChannelManager when selecting channels.
Can a single peer bridge multiple geohash cells simultaneously?
According to the current implementation in permissionlesstech/bitchat, each peer advertises a single bridgeGeohash value per announcement packet. To bridge multiple cells, a peer would need to either rotate advertisements or multiple bridge-enabled peers would need to cover different cells in the same mesh.
Does using a bridge compromise message privacy or authenticity?
No. The bridge node signs forwarded events as a regular Nostr client, but message content remains encrypted using the same cryptographic primitives as direct Nostr communication. The bridge merely acts as a transport layer translator and does not possess special decryption capabilities.
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 →