bitchat 1.7.1 is a security and reliability release. Every change below was field-verified on devices before tagging.
Security
- Noise sessions are bound to claimed peer identities, and leave messages must be signed (#1432). A peer can no longer complete a handshake claiming someone else's ID and receive DMs encrypted for them.
- Private media is encrypted before BLE fragmentation (#1434) — DM photos and voice notes no longer cross the mesh as plaintext; relays never see the bytes.
- Announce signing keys are TOFU-pinned against mesh identity spoofing (#1349). Relayed public messages from a claimed sender are rejected until a verified announce is seen — fail closed.
- Identity keys are now device-bound (
ThisDeviceOnly, never synchronized) (#1431). They cannot be extracted via backup or cloud sync — and consequently an identity no longer survives migrating to a new phone or deleting and reinstalling the app. Existing installs migrate automatically on first unlock; nothing to do on upgrade. - Panic wipe is deterministic and complete (#1431), and what a locked or seized device reveals is reduced across the board (#1484, #1552 — media now carries an explicit file-protection class, chosen so locked-device mesh receive keeps working).
- Shared content requires review before import (#1430). Hostile-network resilience plus per-release source verifiability (#1486, #1488) — every tagged release ships a
SOURCE-MANIFEST.txt; seedocs/VERIFYING-A-BUILD.md.
Reliability
- The DM pipeline survives the hard cases: atomic Noise reconnects, early-ciphertext preservation, durable delivery receipts, retry after key replacement, transactional media deletion, conversations kept canonical across transport aliases and alive across Bluetooth toggles (#1460–#1468, #1462).
- BLE transport re-architecture into a single engine domain with a deterministic multi-node simulation harness (#1498 series). Peer-ID rotation no longer leaves ghost identities — on either link of a dual-link pair (#1554) or across a reconnect after rotation (#1566).
- A restore-path deadlock that could freeze the app on relaunch is fixed (#1425), and a courier-storm amplification with it.
New & fixed
- Persian localization (فارسی) and an in-app language picker (#1443).
- Nearby notes are tap-to-reveal — the location subscription is consent-gated (#1422).
- Verified seal next to names you've verified (#1506); blocked people excluded from @-mention suggestions (#1543).
- The composer keeps focus after sending with the return key (#1490); low-precision geohashes resolve at country level (#1044); people-sheet environment hardening (#1567).
What's Changed
- Quality pass on the 1.7.0 batch: fix confirmed bugs, bump to 1.7.1 by @jackjackbits in #1418
- Bridge dedup: content-derived stable mesh message ID by @jackjackbits in #1419
- PTT hardening: per-peer burst keys + live-capture storage quota by @jackjackbits in #1420
- Close forged-directness link-rebind DoS on DM routing by @jackjackbits in #1421
- Nearby notes: tap-to-reveal (consent-gate the location subscription) + subscription hygiene by @jackjackbits in #1422
- Fix restore-path main↔bleQueue deadlock and courier drop amplification by @jackjackbits in #1425
- Consolidate duplicate BLE links after restore; duplicate fragment-stream suppression; onChange coalescing by @jackjackbits in #1426
- Harden PTT audio and the 1.7.1 release by @jackjackbits in #1423
- fix: use country-level resolution for low-precision geohashes by @malkovitc in #1044
- Harden public intake bounds against untrusted growth by @Chessing234 in #1451
- Add Persian (fa) localization and an in-app language picker by @RodsF1 in #1443
- Make panic wipe deterministic and device-bound by @jackjackbits in #1431
- Bind Noise sessions to claimed peer identities by @jackjackbits in #1432
- Run iOS tests and repair coverage reporting by @jackjackbits in #1429
- Make developer cleanup artifact-only by @jackjackbits in #1433
- Avoid index rebuilds at conversation cap by @jackjackbits in #1435
- Route GeoRelay updates through reviewed data by @jackjackbits in #1436
- perf: verify Nostr inbound signatures once, off the main actor by @jackjackbits in #1352
- Fix BLE identity state races by @jackjackbits in #1428
- Pin announce signing keys to stop mesh identity spoofing by @jackjackbits in #1349
- Require review before importing shared content by @jackjackbits in #1430
- Deflake GeoRelayDirectoryTests under constrained runners by @jackjackbits in #1479
- Encrypt private media before BLE fragmentation by @jackjackbits in #1434
- Make nearby notes tests parallel-safe by @jackjackbits in #1471
- Relabel private Nostr envelopes honestly in docs; harden legacy envelope validation by @jackjackbits in #1480
- Make ordinary Noise reconnects atomic and race-safe by @jackjackbits in #1463
- Preserve early Noise ciphertext across reconnect promotion by @jackjackbits in #1464
- Deliver live DM transport events synchronously by @jackjackbits in #1465
- Persist authenticated private-media delivery receipts by @jackjackbits in #1466
- Retry confirmed private media after reconnect by @jackjackbits in #1467
- Make private-media deletion transactional by @jackjackbits in #1468
- Keep open DMs alive across Bluetooth settings by @jackjackbits in #1460
- Keep DMs canonical across transport aliases by @jackjackbits in #1461
- Retry unacknowledged DMs after Noise replacement by @jackjackbits in #1462
- Harden iOS-sim CI: destination fallback + Noise reconnect test determinism by @jackjackbits in #1483
- docs: correct inaccurate privacy and metadata claims by @jackjackbits in #1485
- Harden what a locked or seized device gives away by @jackjackbits in #1484
- Keep working when the network is hostile, and make the app verifiable by @jackjackbits in #1486
- Close the three holes the #1486 review found by @jackjackbits in #1488
- Keep the composer focused after sending with the return key by @jackjackbits in #1490
- Add a real security policy by @jackjackbits in #1489
- Deflake CI, and make the flake class unrepeatable by @jackjackbits in #1491
- Add Play Store link to README by @sashko in #1524
- BLE transport architecture V3: one engine domain, capability ports, feature-owned state by @jackjackbits in #1498
- Defer alert-binding dismissal writes out of the view update by @jackjackbits in #1537
- Link layer slice 1: extract the central-role radio policy into BLERadioController by @jackjackbits in #1539
- Link layer slice 2: cohere link-auth state and split bindings from the physical store by @jackjackbits in #1540
- Link layer slice 3: bindings and link-auth become engine-owned (the option-B domain flip) by @jackjackbits in #1547
- Link layer slice 4: deterministic multi-node mesh simulation (and the panic-announce bug it caught) by @jackjackbits in #1548
- Link layer slice 5: BLELinkEvent — the port has a name, the delegates have their own files by @jackjackbits in #1551
- Fix #1538: release stale bindings on rotation instead of leaving a ghost identity by @jackjackbits in #1554
- Fix $$ escaping that broke every Xcode just recipe by @jozanek in #1525
- Fix built-in relay actor isolation by @kudala-bharani in #1528
- Don't suggest blocked people in @-mentions by @Chessing234 in #1543
- Make DeliveryStatus non-optional with an explicit .notSentYet state by @viditkulsh in #1503
- Replace try! regex construction with a non-trapping SafeRegex helper by @viditkulsh in #1501
- fix: show verified seal next to sender names in chat by @Chessing234 in #1506
- chore: fix receive typo and refresh relay count by @Chessing234 in #1510
- Normalize nicknames to Unicode NFC at storage and comparison boundaries by @viditkulsh in #1502
- fix(ble): don't spend the fragment scheduler's slot budget on blocked requests by @krishrathi1 in #1530
- Make the completion-grace restart test deterministic by @jackjackbits in #1563
- Fix the SimulatedMesh announce-loss flake by @jackjackbits in #1564
- Fix the retire↔reconnect oscillation: redundant-link survivor is the newest connection by @jackjackbits in #1566
- fix: stop EnvironmentObject crash in the people sheet by @Chessing234 in #1567
- Give media the explicit file-protection class other stores use by @heyaim in #1552
- Pin that unknown file TLVs are skipped, not fatal by @vincenzopalazzo in #1550
- Deflake VoiceRecorderTests: replace timed semaphores with async events by @jackjackbits in #1572
- Silence the four release-build warnings by @jackjackbits in #1583
New Contributors
- @Chessing234 made their first contribution in #1451
- @RodsF1 made their first contribution in #1443
- @sashko made their first contribution in #1524
- @jozanek made their first contribution in #1525
- @kudala-bharani made their first contribution in #1528
- @viditkulsh made their first contribution in #1503
- @krishrathi1 made their first contribution in #1530
- @heyaim made their first contribution in #1552
- @vincenzopalazzo made their first contribution in #1550
Full Changelog: v1.7.0...v1.7.1