Up-to-date mirror of Bitchat software
  • Swift 99%
  • Shell 0.4%
  • Python 0.3%
  • Rust 0.2%
Find a file
jack 9b84b36122
Localize the last hardcoded UI strings; wire up two dead translations (#1656)
* Add 26 code-referenced keys to the catalog + a test that closes the gap

LocalizationCoverageTests validated the catalog but never the code: a
String(localized:) whose key is missing from every catalog compiles
fine and silently ships its English defaultValue to all 29 non-source
locales. That blind spot let 26 keys go untranslated while CI stayed
green: the entire notices/board composer (10), the private-media
encryption warnings and delivery-failure reasons (9), both
courier-header strings, two delivery states, two media failure
reasons, and the [? people] channel row.

- All 26 keys added with full 30-locale coverage.
- New everyCodeReferencedKeyExistsInACatalog scans the source for
  literal String(localized:) keys and fails on any key absent from
  both catalogs. (The audit's original count of 17 was itself an
  undercount — its scan missed multi-line String( localized: calls;
  the test's regex does not.)

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

* Localize CommandProcessor: all 48 command strings, 30 locales

CommandProcessor had zero String(localized:) calls — every command
error, usage hint, and the entire /help reference was hardcoded
English. The autocomplete panel (CommandInfo) is fully localized, so a
non-English speaker got localized suggestions while typing and English
the moment anything went wrong or they asked for help.

- 47 literal sites converted to String(localized:)/String(format:)
  with positional specifiers where languages reorder (%1$@, %2$lld).
- /trace's "hop"/"hops" pluralization split into path_one/path_many;
  the "you" chain label localized too.
- helpText and groupUsage become computed so they resolve per-locale.
- 49 command.* keys added with full 30-locale coverage (command
  syntax stays verbatim; only prose translates).
- Copy fix from the audit: "blocked X. you will no longer receive
  messages from them" → "…no longer see their messages" (blocking
  filters at display time; packets still arrive and relay).

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

* Localize the last hardcoded UI strings; wire up two dead translations

The stragglers from the unlocalized-strings audit:

- Nearby push notification: "bitchatters nearby!" title and the
  hand-rolled English pluralization ("1 person around" / "N people
  around") now localize via body_one/body_many.
- Voice recording errors (mic permission, start/too-short/save
  failures) and the "Recording Error" alert title — also brought into
  the lowercase house voice.
- The recording HUD's Cancel label now uses common.cancel.
- "No image selected" in both image pickers.
- The DM screenshot echo "you took a screenshot" (the wire-format
  sibling stays English for Android compat).
- "Voice/Images are only available in mesh chats." had fully
  translated catalog entries in all 30 locales that were never looked
  up — the call sites passed the raw string instead of resolving the
  key. Wired up via String(localized:).

10 new keys x 30 locales.

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-08-10 20:40:14 +02:00
.github/workflows Make the Periphery scan blocking (#1648) 2026-08-09 11:39:03 +02:00
bitchat Localize the last hardcoded UI strings; wire up two dead translations (#1656) 2026-08-10 20:40:14 +02:00
bitchat.xcodeproj Link layer slice 4: deterministic multi-node mesh simulation (and the panic-announce bug it caught) (#1548) 2026-07-30 01:14:19 +01:00
bitchatShareExtension Require review before importing shared content (#1430) 2026-07-26 11:40:17 +02:00
bitchatTests Localize CommandProcessor: all 48 command strings, 30 locales (#1657) 2026-08-10 20:21:02 +02:00
Configs Make developer cleanup artifact-only (#1433) 2026-07-26 10:50:25 +02:00
docs Peer ID rotation: working primitives + spec for iOS/Android review (#1487) 2026-08-01 09:41:15 +01:00
localPackages Make SwiftLint blocking in CI now that the violation backlog is zero (#1646) 2026-08-08 12:12:30 +02:00
relays Automated update of relay data - Sun Jul 26 06:54:16 UTC 2026 2026-07-26 06:54:16 +00:00
scripts Route GeoRelay updates through reviewed data (#1436) 2026-07-26 10:55:07 +02:00
.gitattributes Prevent Github Languages stats skewing (#630) 2025-09-16 06:42:36 -07:00
.gitignore Pin announce signing keys to stop mesh identity spoofing (#1349) 2026-07-26 11:38:24 +02:00
.periphery.baseline.json Link layer slice 5: BLELinkEvent — the port has a name, the delegates have their own files (#1551) 2026-07-30 09:27:34 +01:00
.periphery.yml Quality pass on the 1.7.0 batch: fix confirmed bugs, bump to 1.7.1 (#1418) 2026-07-09 16:44:02 +02:00
.swiftlint.yml Make SwiftLint blocking in CI now that the violation backlog is zero (#1646) 2026-08-08 12:12:30 +02:00
BRING_THE_NOISE.md Perf: Add final to classes that are not inherited (#574) 2025-09-11 19:17:04 +02:00
Justfile Fix $$ escaping that broke every Xcode just recipe (#1525) 2026-07-30 17:56:32 +01:00
LICENSE Add public domain license and copyright headers 2025-07-04 16:21:49 +02:00
Package.resolved fix: add secp256k1 dependency to resolve spm import error (#429) 2025-08-11 22:09:19 +02:00
Package.swift Relabel private Nostr envelopes honestly in docs; harden legacy envelope validation (#1480) 2026-07-26 12:50:17 +02:00
PRIVACY_POLICY.md Keep working when the network is hostile, and make the app verifiable (#1486) 2026-07-26 19:45:37 +02:00
README.md chore: fix receive typo and refresh relay count in README (#1510) 2026-07-30 17:56:52 +01:00
SECURITY.md Add a real security policy (#1489) 2026-07-26 21:33:59 +02:00
WHITEPAPER.md docs: correct inaccurate privacy and metadata claims (#1485) 2026-07-26 19:02:26 +02: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

📲 Play Store

Getting a copy you can trust

Install from the App Store, or build from source you have verified. A compiled build from anywhere else cannot be verified — see Verifying bitchat for how to check source against the per-release hash manifest, and for what to do if that is the only build you can get.

This matters more than it usually would: this repository has been the target of takedown demands, and when a repository or releases page disappears, mirrors appear that nobody can check.

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 servers. Note that the mesh does use a persistent per-device identifier derived from your identity key — see the whitepaper on identity and metadata for what a nearby radio can observe
  • Private Message End-to-End Encryption: Noise Protocol for mesh, BitChat private envelopes for Nostr fallback
  • 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 for live sessions (store-and-forward mail is sealed without it — see the whitepaper)
  • 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
  • 440+ Relay Network: Distributed across the globe for reliability
  • BitChat Private Envelopes: App-specific encrypted private messages over Nostr relays
  • Ephemeral Keys: Fresh cryptographic identity per geohash area

BitChat's private-envelope format is proprietary and is not NIP-17, NIP-44, or NIP-59 compatible. It uses Nostr as a relay transport but only interoperates with BitChat clients: private payloads travel inside kind-1059 events whose v2:-prefixed content is a BitChat-specific XChaCha20-Poly1305 construction, not NIP-44 encryption.

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
    • BitChat's app-specific private-envelope encryption
    • 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

open bitchat.xcodeproj

For a signed device build, create your ignored local configuration and replace the example team ID with your Apple Developer Team ID:

cp Configs/Local.xcconfig.example Configs/Local.xcconfig

Local.xcconfig.example derives unique app and App Group identifiers from that team ID. The entitlement files already reference $(APP_GROUP_ID), so tracked project or entitlement files do not need to be edited.

Useful command-line checks from the repository root:

# macOS Debug build without signing
xcodebuild -project bitchat.xcodeproj -scheme "bitchat (macOS)" \
  -configuration Debug CODE_SIGNING_ALLOWED=NO build

# Full SwiftPM test suite
swift test

# iOS simulator tests
xcodebuild -project bitchat.xcodeproj -scheme "bitchat (iOS)" \
  -sdk iphonesimulator \
  -destination 'platform=iOS Simulator,name=iPhone 17' test

If iPhone 17 is unavailable, choose an installed simulator from:

xcodebuild -showdestinations -project bitchat.xcodeproj -scheme "bitchat (iOS)"

Option 2: Using just

brew install just
just check
just run

just build and just run use the current bitchat (macOS) scheme and keep Xcode output in the ignored .DerivedData/ directory. They never patch source, project, configuration, or entitlement files.

just clean removes only .DerivedData/ and .build/. It does not invoke Git or restore tracked files, so uncommitted work is preserved. just test runs the SwiftPM suite and just test-ios runs the iPhone 17 simulator suite.

Localization

  • App localizations live in bitchat/Localizable.xcstrings.
  • Share extension strings are separate in bitchatShareExtension/Localization/Localizable.xcstrings.
  • 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.