d4f0c49787 Gateway mode: opt-in mesh↔Nostr uplink for geohash channels (#1384)
* Add capability bits to announce TLV

Announces now carry an optional capabilities TLV (0x05): a little-endian
bitfield with named bits for upcoming features (prekeys, wifiBulk,
gateway, groups, board, vouch, meshDiagnostics). Old clients skip the
unknown TLV; peers without it decode as nil so features can distinguish
"legacy peer" from "advertises nothing".

PeerCapabilities lives in BitFoundation with a minimal-length encoding
that preserves unknown bits for forward compatibility. Peer capabilities
are stored in the BLE peer registry on verified announce and exposed via
BLEService.peerCapabilities(_:). The local advertisement set is empty
until each feature ships its bit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Gateway mode: opt-in mesh↔Nostr uplink for geohash channels

An opt-in "internet gateway" toggle lets one connected phone bridge the
local geohash channel for mesh-only peers: signed kind-20000 events ride
a new nostrCarrier (0x28) packet — directed to the gateway for uplink,
broadcast with TTL for downlink — with Schnorr verification at every
hop, CourierStore-style quotas, and explicit loop-prevention rules.

- BitFoundation: MessageType.nostrCarrier = 0x28
- NostrCarrierPacket: 2-byte-length TLV codec (direction, geohash,
  signed event JSON), 16 KiB cap, tolerant decoder
- GatewayService: closure-injected policy layer — verify gates (sig,
  kind, #g tag, age, size), uplink quotas (10/min/depositor rate limit,
  offline queue of 20 total / 5 per depositor, drop-oldest, flush on
  reconnect), downlink budget (30/min, bounded drop-oldest backlog),
  bounded loop-prevention ID sets
- BLEService: runtime capability bits (advertise .gateway only while
  the toggle is on, re-announce on change), signed directed uplink
  sends, carrier ingress with depositor signature verification
- Mesh-only senders uplink automatically from sendGeohash when no relay
  is connected and a reachable peer advertises .gateway; once-per-
  channel "sent via mesh gateway" notice
- UI: gateway toggle beside the Tor toggle, globe header indicator,
  VoiceOver labels, xcstrings entries

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Gateway: harden downlink freshness, uplink verify ordering, and drain

Fixes the confirmed downlink/uplink defects from the PR #1384 review +
Codex findings:

- Downlink age + #g gate (Codex P2 / review #1): rebroadcastRelayEvent
  now drops events outside the same freshness window receivers enforce
  and whose #g tag mismatches the carrier geohash, BEFORE spending any
  budget — so a 1h/200-event channel-resubscribe backfill no longer
  burns the 30/min BLE budget on events every receiver drops.
- Rate-limit + dedup before Schnorr (review #2): handleUplinkDeposit now
  runs cheap structural checks + carried-ID dedup + rate-token consume
  before isValidSignature(), so a replay flood is bounded by cheap work
  instead of unbounded main-actor verifies.
- Quota-dropped deposits not rendered (review #3): enqueueUplink reports
  acceptance and injectInbound only fires for events actually
  published/queued, ending the local-timeline divergence.
- Drain timer + mark-after-send (Codex P2 / review #4): a burst beyond
  budget now arms a timer to drain when the window frees; rebroadcast
  IDs are marked only after an event is actually sent, so overflow-
  dropped events stay retryable.
- Symmetric publish path (review #5): the gateway publish closure now
  refuses when no geo relay is known, matching the local send path
  instead of publishing dead traffic to default relays.
- Loop-rule doc (review #7): softened to reflect that rule 3 is a
  call-site convention with unit-tested backstops; added tests for the
  publishedEventIDs backstop, downlink freshness/mismatch, drain timer,
  and quota-drop non-injection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Gateway: stop self-echo of uplinked events onto the mesh

Every event a gateway uplinks to the relays comes back through its own
geohash subscription. `rebroadcastRelayEvent` deduped against
`meshBroadcastEventIDs`, `rebroadcastEventIDs`, and `pendingDownlinks`,
but not `publishedEventIDs` — so an event this gateway just published
was downlink-rebroadcast onto the same mesh it originated from, doubling
BLE airtime per uplinked message and able to starve the 30/min downlink
budget on a busy channel (device-confirmed, filed on #1384).

Fix: also skip the downlink rebroadcast when the event id is in
`publishedEventIDs`. That set is already the bounded (drop-oldest,
capacity maxTrackedEventIDs) loop-rule-2 uplink cache, populated only by
`publish()`, so genuine inbound-from-internet events (never published
here) still rebroadcast normally. Reconciles cleanly with the existing
loop-prevention sets — no new state.

Adds a GatewayServiceTests case asserting an uplinked event that echoes
back via the subscription is not rebroadcast, while a genuine inbound
event still is.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:55:17 +02:00
2026-04-05 19:04:18 -05:00
2026-01-14 14:39:39 -10:00

icon_128x128@2x

bitchat

A decentralized peer-to-peer messaging app with dual transport architecture: local Bluetooth mesh networks for offline communication and internet-based Nostr protocol for global reach. No accounts, no phone numbers, no central servers. It's the side-groupchat.

bitchat.free

📲 App Store

License

This project is released into the public domain. See the LICENSE file for details.

Features

  • Dual Transport Architecture: Bluetooth mesh for offline + Nostr protocol for internet-based messaging
  • Location-Based Channels: Geographic chat rooms using geohash coordinates over global Nostr relays
  • Intelligent Message Routing: Automatically chooses best transport (Bluetooth → Nostr fallback)
  • Decentralized Mesh Network: Automatic peer discovery and multi-hop message relay over Bluetooth LE
  • Privacy First: No accounts, no phone numbers, no persistent identifiers
  • Private Message End-to-End Encryption: Noise Protocol for mesh, NIP-17 for Nostr
  • IRC-Style Commands: Familiar /slap, /msg, /who style interface
  • Universal App: Native support for iOS and macOS
  • Emergency Wipe: Triple-tap to instantly clear all data
  • Performance Optimizations: LZ4 message compression, adaptive battery modes, and optimized networking

Technical Architecture

BitChat uses a hybrid messaging architecture with two complementary transport layers:

Bluetooth Mesh Network (Offline)

  • Local Communication: Direct peer-to-peer within Bluetooth range
  • Multi-hop Relay: Messages route through nearby devices (max 7 hops)
  • No Internet Required: Works completely offline in disaster scenarios
  • Noise Protocol Encryption: End-to-end encryption with forward secrecy
  • Binary Protocol: Compact packet format optimized for Bluetooth LE constraints
  • Automatic Discovery: Peer discovery and connection management
  • Adaptive Power: Battery-optimized duty cycling

Nostr Protocol (Internet)

  • Global Reach: Connect with users worldwide via internet relays
  • Location Channels: Geographic chat rooms using geohash coordinates
  • 290+ Relay Network: Distributed across the globe for reliability
  • NIP-17 Encryption: Gift-wrapped private messages for internet privacy
  • Ephemeral Keys: Fresh cryptographic identity per geohash area

Channel Types

mesh #bluetooth

  • Transport: Bluetooth Low Energy mesh network
  • Scope: Local devices within multi-hop range
  • Internet: Not required
  • Use Case: Offline communication, protests, disasters, remote areas

Location Channels (block #dr5rsj7, neighborhood #dr5rs, country #dr)

  • Transport: Nostr protocol over internet
  • Scope: Geographic areas defined by geohash precision
    • block (7 chars): City block level
    • neighborhood (6 chars): District/neighborhood
    • city (5 chars): City level
    • province (4 chars): State/province
    • region (2 chars): Country/large region
  • Internet: Required (connects to Nostr relays)
  • Use Case: Location-based community chat, local events, regional discussions

Direct Message Routing

Private messages use intelligent transport selection:

  1. Bluetooth First (preferred when available)

    • Direct connection with established Noise session
    • Fastest and most private option
  2. Nostr Fallback (when Bluetooth unavailable)

    • Uses recipient's Nostr public key
    • NIP-17 gift-wrapping for privacy
    • Routes through global relay network
  3. Smart Queuing (when neither available)

    • Messages queued until transport becomes available
    • Automatic delivery when connection established

For detailed protocol documentation, see the Technical Whitepaper.

Setup

Option 1: Using Xcode

cd bitchat
open bitchat.xcodeproj

To run on a device there're a few steps to prepare the code:

  • Clone the local configs: cp Configs/Local.xcconfig.example Configs/Local.xcconfig
  • Add your Developer Team ID into the newly created Configs/Local.xcconfig
    • Bundle ID would be set to chat.bitchat.<team_id> (unless you set to something else)
  • Entitlements need to be updated manually (TODO: Automate):
    • Search and replace group.chat.bitchat with group.<your_bundle_id> (e.g. group.chat.bitchat.ABC123)

Option 2: Using just

brew install just

Want to try this on macos: just run will set it up and run from source. Run just clean afterwards to restore things to original state for mobile app building and development.

Localization

  • Base app resources live under bitchat/Localization/Base.lproj/. Add new copy to Localizable.strings and plural rules to Localizable.stringsdict.
  • Share extension strings are separate in bitchatShareExtension/Localization/Base.lproj/Localizable.strings.
  • Prefer keys that describe intent (app_info.features.offline.title) and reuse existing ones where possible.
  • Run xcodebuild -project bitchat.xcodeproj -scheme "bitchat (macOS)" -configuration Debug CODE_SIGNING_ALLOWED=NO build to compile-check any localization updates.
S
Description
bluetooth mesh chat, IRC vibes
Readme Unlicense
247 MiB
Languages
Swift 99.1%
Shell 0.4%
Rust 0.3%
Just 0.1%