Bitchat is built around a dual-transport architecture because no single medium serves all situations. Bluetooth LE mesh works offline, requires no infrastructure, and is fast and private for people in the same physical space — but it has a limited range. The Nostr protocol over the internet bridges separate meshes and enables location-based channels across the globe. ADocumentation Index
Fetch the complete documentation index at: https://mintlify.com/permissionlesstech/bitchat/llms.txt
Use this file to discover all available pages before exploring further.
MessageRouter coordinates both transports, choosing the best available path for each message and falling back gracefully when connectivity changes.
Transport Layer
Both the BLE mesh and the Nostr relay network implement a commonTransport protocol. The interface exposes a unified surface for lifecycle management (startServices, stopServices), peer reachability queries (isPeerConnected, isPeerReachable, canDeliverPromptly), and all message-sending operations. Higher-level code in MessageRouter and the view layer never speaks directly to BLE or Nostr internals — they call through this shared contract.
BLEService is the primary Transport implementation. The Nostr transport fills the same role for internet delivery. Both feed events upward through a typed TransportEvent stream (covering messageReceived, peerConnected, peerDisconnected, delivery status updates, and Bluetooth state changes) and through a legacy BitchatDelegate bridge for compatibility during the ongoing architecture migration.
Bluetooth Mesh
The BLE mesh layer requires no Wi-Fi, mobile data, paired accessories, or user accounts. Any two devices running Bitchat within Bluetooth range can communicate immediately. Simultaneous central and peripheral. Every device advertises as a GATT peripheral while scanning as a GATT central at the same time. This means every node is both a sender and a relay — the mesh forms automatically as devices come into range. Multi-hop relay. Packets originate with a Time-To-Live (TTL) of 7, allowing messages to traverse up to 7 relay hops. Dense graphs clamp broadcast TTL at 5; thin chains (two or fewer links) relay at full depth. Controlled flood with deduplication. Each device maintains an LRU seen-set of 1,000 entries with a 5-minute expiry, keyed on sender, timestamp, packet type, and a payload digest. Duplicate packets are dropped before re-relay, and any scheduled relay is cancelled when a duplicate arrives first from another path. Jitter-based relay scheduling. Relays wait a random 10–220 ms before forwarding (wider windows in dense graphs). This temporal spread lets deduplication win most races and reduces redundant transmissions without a central coordinator. Fanout subsetting for broadcasts. Rather than re-sending to every connected peer, broadcast packets are forwarded to a deterministic, message-ID-seeded subset of approximately log₂(degree) peers. The ingress link is always excluded (split horizon). Announcements, fragments, and sync packets use full fanout. Directed traffic (handshakes, private messages, courier envelopes) always relays to the specific destination. No infrastructure or pairing required. The mesh forms entirely from Bluetooth LE advertisement and GATT connections. There are no central brokers, no prior pairing, and no accounts needed.Bluetooth mesh communication requires physical devices with Bluetooth hardware. Running Bitchat in an iOS Simulator or a macOS sandbox without Bluetooth will show the UI but no peers will appear and no mesh packets will be sent or received.
Nostr Protocol
Nostr extends Bitchat’s reach beyond Bluetooth range by using a distributed network of public relay servers connected over the internet. 290+ relay network. Nostr relays are independently operated servers that store and forward signed events. Bitchat subscribes to a broad set of these relays for redundancy; no single relay is required for delivery. Location channels via geohash. Geographic chat rooms are scoped by geohash precision — a short alphanumeric string that encodes a region on Earth. Messages for a given geohash are published to Nostr and received by anyone subscribed to the same cell. This makes it possible to join a neighborhood channel without the other participants being in Bluetooth range. Each geohash area uses ephemeral keys to limit cross-area correlation. Private message fallback. When Bluetooth is unavailable, private messages to mutual favorites travel over Nostr using Bitchat’s proprietary private-envelope protocol. An unsigned inner message (kind 14) is encrypted and placed in a sender-signed seal (kind 13), which is then wrapped inside a public gift-wrap envelope (kind 1059) signed with a one-time key. This layered construction means relays learn neither the stable sender identity nor the content. Outer timestamps are randomized ±15 minutes; the actual message timestamp is encrypted inside.Bitchat’s Nostr private-envelope format is proprietary. It reuses NIP-17/NIP-59 kind numbers for historical reasons but is not NIP-17, NIP-44, or NIP-59 compatible and only interoperates with Bitchat clients.
Message Routing
MessageRouter coordinates all transports and implements an ordered fallback strategy for private messages:
- 1. Bluetooth First
- 2. Nostr Fallback
- 3. Courier Queuing
When a peer has an active Bluetooth connection and an established Noise XX session (
canDeliverSecurely), the message is sent directly over the mesh. This is the fastest and most private path. The message is also retained in the sender outbox until a delivery or read acknowledgement clears it, protecting against session staleness.AppRuntime Architecture
Bitchat v2 introducedAppRuntime as the composition root for the entire application. Rather than wiring startup, lifecycle, and cross-cutting concerns inside ChatViewModel or the SwiftUI app entry point, AppRuntime owns:
- Startup and lifecycle — Tor, screenshot, and Nostr reconnect observer wiring
ConversationStore— the single source of truth for the active public channel and selected private conversation, including private-inbox read state and DM timelines under canonicalConversationIDkeys- Feature-scoped models —
PublicChatModel,PrivateInboxModel,PrivateConversationModel,ConversationUIModel,LocationChannelsModel,PeerListModel, andAppChromeModeleach own a focused slice of view-facing state IdentityResolver— mirrors the peer graph into canonicalPeerHandleidentities above the transport-specific IDsAppEventStream— a typed async event surface for app-level events
ChatViewModel remains the active domain bridge during the incremental migration, but new feature code consumes injected feature-scoped models rather than referencing ChatViewModel as a global environment object. BitchatApp is now reduced to app wiring only.
Identity
How Bitchat generates and stores cryptographic identity — the two keypairs, peer ID derivation, and what a passive radio observer can learn.
Channels
The three channel types — Bluetooth mesh, private groups, and Nostr location channels — and when to use each.