Shubham Bhandari a01fff5aed Three review findings, all confirmed and all mine
Codex raised three P1s on the first revision. I checked each against the code
before touching anything; all three are real, and the first two are the same
mistake in two costumes — one predicate asked two different questions.

1. An announced suffix is not a UI decoration.

`sealApplies` stripped a trailing `#abcd` from whatever it was given, including
the LIVE announced nickname. A peer announces whatever string it likes, and `#`
plus four hex is a legal thing to announce — this app's own `splitSuffix`
exists because announced names carry them. So the single predicate was wrong in
both directions at once: a key pinned as `medic` could rename to `medic#cafe`
and keep its seal, and a key honestly verified as `medic#cafe` lost its seal
without renaming at all.

There are now two functions, `sealAppliesToAnnounced` and
`sealAppliesToRendered`, because there are two questions: what a peer claims,
and what a row shows after the list may have decorated it. The rendered one
tries the raw name before undecorating, so a name that genuinely ends in a
suffix still matches itself. The iOS patch has exactly this split and I
collapsed it here.

2. The check was not reactive.

`PeopleSection` caches `isPeerVerified` with
`remember(verifiedFingerprints, peerFingerprints, connectedPeers)`, and the
per-peer sheet with `remember(peerID, verifiedFingerprints)`. My gate reads the
announced nickname inside those lambdas, and a rename changes none of those
keys — so the NAME repainted while the cached `true`, and the seal, survived
until something unrelated invalidated the cache. That is the connected peer
still showing a seal after renaming: the live impersonation, and the single
case this patch exists to stop. `peerNicknames` is now a key at both sites.

3. The watch was never patched at all.

There is a `:wear` module in settings.gradle.kts with its own
`WearPeerIdentityState`, its own verify button and its own seal, and it calls
`setVerifiedFingerprint` without a nickname while deriving `isVerified` from
the fingerprint set alone. Every peer verified on a watch failed open for ever.
I missed it because I audited `app/` and assumed that was the app — I never
read settings.gradle.kts. It now pins the announced name on verify and applies
the binding in `snapshot`.

15 binding cases, up from 13. Mutation-checked: put the stripping back into the
announced check and the new case fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 16:59:01 +08:00
2026-08-11 19:33:44 +02:00
2025-07-08 20:37:46 +02:00
2025-07-08 20:37:46 +02:00
2026-08-12 01:55:05 +02:00

icon_128x128@2x

bitchat for Android

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.

This is the Android implementation of bitchat, fully protocol-compatible with the iOS version for cross-platform mesh communication.

bitchat.free

GitHub Releases

Get it on Google Play

See it in action

Offline mesh conversation Geohash globe picker
Active four-peer Bitchat mesh conversation with an image, voice messages, and text messages Bitchat geohash location picker showing the whole Earth and geohash grid

License

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

Features

  • Dual Transport Architecture: Bluetooth LE mesh for offline messaging, Nostr relays for internet-based messaging
  • Location-Based Channels: Geographic chat rooms using geohash coordinates over Nostr relays
  • Intelligent Message Routing: Automatically chooses the best transport, with queuing and retry when a peer is unreachable
  • End-to-End Encryption: Noise Protocol (XX pattern, X25519 + ChaCha20-Poly1305) for private messages over the mesh
  • Decentralized Mesh Network: Automatic peer discovery and multi-hop relay over Bluetooth LE (max 7 hops)
  • Wi-Fi Aware Transport: Higher-bandwidth local mesh on supported devices
  • Channel Chats: Topic-based group messaging with optional password protection (Argon2id + AES-256-GCM)
  • IRC-Style Commands: Familiar /join, /msg, /who style interface
  • Tor Support: Built-in Tor (Arti) for private internet connectivity
  • Emergency Wipe: Triple-tap to instantly clear all data
  • Cross-Platform: Binary protocol compatible with bitchat on iOS and macOS

Technical Architecture

Bluetooth Mesh Network (Offline)

  • Direct peer-to-peer within Bluetooth range, multi-hop relay through nearby devices
  • Noise Protocol sessions with forward secrecy; peer identities derived from static keys
  • Compact binary packet format with fragmentation, TTL routing, and deduplication
  • Adaptive duty cycling and connection limits for battery efficiency
  • Foreground service keeps the mesh alive within Android background execution limits

Nostr Protocol (Internet)

  • Global reach via public relays, geohash-based location channels
  • Private messages fall back to Nostr for mutual favorites when the mesh is unavailable
  • Ephemeral keys per geohash area

Android Stack

  • Kotlin, Jetpack Compose (Material 3), MVVM
  • Coroutines and Flow for all networking and state
  • Core components: MeshForegroundService (persistent connectivity), BluetoothMeshService / WifiAwareMeshService (transports), UnifiedMeshService (transport selection), NoiseSessionManager (encryption sessions), MessageRouter (mesh/Nostr routing with outbox retry)

Building

Requires Android Studio and the Android SDK (API 26+).

git clone https://github.com/permissionlesstech/bitchat-android.git
cd bitchat-android
./gradlew assembleDebug

Install on a connected device:

adb install -r app/build/outputs/apk/debug/app-debug.apk

The app requests Bluetooth, location (required for BLE scanning), and notification permissions at runtime.

Release APKs and the Android App Bundle can be rebuilt byte-for-byte in the pinned Linux container. Maintainers should follow the Android release guide. See Reproducible builds for the build trust model and public GitHub/Google Play verification procedures.

Testing

# Unit tests
./gradlew test

# Lint
./gradlew lint

# Instrumented tests (requires a device or emulator)
./gradlew connectedAndroidTest

Note that BLE mesh behavior is difficult to emulate; protocol and session logic is covered by unit tests, while radio-level behavior needs real devices.

Description
bluetooth mesh chat, IRC vibes
Readme
Languages
Kotlin 86.1%
Java 9.9%
Python 2.5%
Shell 1.1%
Rust 0.3%
Other 0.1%