mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 22:25:18 +00:00
Centralize PTT and voice audio-session ownership, harden courier/bridge/outbox delivery and recovery, correct location and delivery-state races, add privacy/release metadata, and ship reproducible universal Arti slices with Release CI coverage. Validated by the full iOS suite, repeated audio/fragment/performance regressions, BitFoundation tests, strict lint and dead-code analysis, universal iOS Release builds, and iOS/macOS archives.
100 lines
7.8 KiB
Markdown
100 lines
7.8 KiB
Markdown
# bitchat Privacy Assessment
|
|
|
|
Last reviewed: July 2026
|
|
|
|
## Scope
|
|
|
|
- BLE discovery, mesh routing, gossip sync, private delivery, and courier behavior
|
|
- Nostr private fallback, bridge courier drops, mesh bridging, geohash channels, and notices
|
|
- CoreLocation and reverse geocoding
|
|
- Local persistence, panic wipe, logging, and App Store privacy manifests
|
|
|
|
The user-facing contract is `PRIVACY_POLICY.md`. This document records implementation-level behavior and residual risks that should be re-audited when storage or transport semantics change.
|
|
|
|
## Current Posture
|
|
|
|
- The project operates no account system, analytics pipeline, advertising SDK, or project-owned messaging backend.
|
|
- Mesh transport is peer-to-peer. Optional internet features use third-party Nostr relays and can expose public content, coarse geohashes, timing, relay, and network metadata.
|
|
- Private payloads are end-to-end encrypted, but public mesh, board, bridge, and geohash content is intentionally visible to its participants.
|
|
- Local storage is bounded where practical and included in panic wipe, but it is not wholly ephemeral. The app persists the stores listed below.
|
|
- The app and share extension each bundle a privacy manifest declaring their actual required-reason API use.
|
|
|
|
## BLE Discovery and Metadata
|
|
|
|
Signed announces can expose:
|
|
|
|
- Nickname, persistent Noise public key, and Ed25519 signing public key
|
|
- Capability flags
|
|
- A bounded set of short direct-neighbor identifiers
|
|
- A coarse rendezvous geohash when the bridge capability is enabled
|
|
|
|
The app does not advertise the device's user-assigned name. iOS manages BLE address randomization; bitchat does not attempt to create a stable MAC address. RSSI, timing, traffic volume, and radio fingerprints remain observable to nearby receivers.
|
|
|
|
Ingress validates announce structure, sender binding, signatures, payload sizes, and freshness. Current-link Noise authentication is required before destructive courier handoff or strict directed delivery. Floods, queues, fragments, ingress work, and per-peer state are bounded.
|
|
|
|
## Private Messaging and Courier Delivery
|
|
|
|
- Direct mesh sessions use Noise XX with X25519, ChaCha20-Poly1305, and SHA-256.
|
|
- Undelivered outgoing private messages remain in a bounded, ChaChaPoly-sealed outbox for at most 24 hours. Its key is stored in the keychain.
|
|
- Physical couriers store opaque Noise-sealed envelopes, not plaintext. Deposits have trust-tier quotas, per-depositor caps, a global cap, and at most a 24-hour lifetime.
|
|
- Spray-and-wait copies are bounded and progress cannot be replenished by replaying a deposit.
|
|
- Delivery status advances only after real transport admission or explicit relay acceptance; late failure cannot downgrade a delivered/read message.
|
|
- Panic wipe deletes the outbox, courier mail, keys, dedup state, and active transport state.
|
|
|
|
Residual risk: private-message metadata such as timing, radio adjacency, ciphertext size, rotating recipient tags, and relay connections remains observable. A compromised recipient device can disclose plaintext.
|
|
|
|
## Public Gossip, Boards, and Media
|
|
|
|
- Recent signed public mesh messages are archived in Application Support for up to 15 minutes so gossip sync survives a relaunch and can cross mesh partitions.
|
|
- Signed public board posts and tombstones persist until author-selected expiry, at most seven days. Stores are bounded by global and per-author quotas.
|
|
- Group metadata (name, roster, creator, epoch) persists as protected JSON; group keys live in the keychain until leave/removal/wipe.
|
|
- Voice notes and images are stored in Application Support. Incoming media has a 100 MB oldest-first quota; outgoing media does not have an equivalent automatic lifetime and remains until cleanup, panic wipe, or app removal.
|
|
|
|
Public archives contain content already intended for public mesh/board distribution, but a seized unlocked device can reveal it. Group metadata and media can reveal relationships or content even when the in-memory chat timeline has gone away.
|
|
|
|
## Nostr and Mesh Bridge
|
|
|
|
- NIP-17/NIP-44 v2 private fallback protects plaintext with secp256k1 key agreement, HKDF-SHA256, and XChaCha20-Poly1305. Relays still see event and network metadata.
|
|
- Bridge courier drops use a throwaway publisher key, an opaque Noise-sealed envelope, and a day-rotating recipient tag. Only a party already holding the recipient's Noise static key can compute candidate tags.
|
|
- Relay publication is considered successful only after an explicit NIP-20 `OK true` from at least one target relay. Rejected, disconnected, timed-out, or merely socket-written events stay retryable.
|
|
- When mesh bridge is enabled, public mesh messages not marked “nearby only” are signed under a per-cell Nostr identity and published to a neighborhood rendezvous geohash. Presence and public bridge traffic therefore expose a coarse area to relays and participants.
|
|
- A bridge gateway can carry signed bridge/location events and opaque courier drops for nearby mesh-only peers. It cannot validly publish a neighbor's radio-only message because the author must first sign the bridge event.
|
|
|
|
Residual risk: Nostr relay retention and logging are outside project control. Public events may be copied indefinitely. Timing, coarse location, and participation can be correlated even when content is encrypted or per-cell identities are used.
|
|
|
|
## Location
|
|
|
|
- When-in-use CoreLocation access computes geohash choices and bridge cells. Permission revocation stops live sampling and releases subscriptions.
|
|
- Exact coordinates are not persisted by bitchat or placed into mesh/Nostr payloads.
|
|
- Selected/bookmarked geohashes, teleport flags, and display names persist in local preferences; a fine geohash can identify a small area.
|
|
- Friendly place names use `CLGeocoder.reverseGeocodeLocation`. Apple may process the supplied location under its own privacy terms, so this operation is not accurately described as wholly on-device.
|
|
- Automatic presence is limited to lower-precision geohashes; precise posts occur through user-selected channels, notes, notices, or the bridge behavior presented in the UI.
|
|
|
|
## Logging and Telemetry
|
|
|
|
- `SecureLogger` uses OSLog privacy markers and filters likely secrets. Release builds suppress debug verbosity.
|
|
- No project analytics or telemetry endpoint exists.
|
|
- Apple system logs, Nostr relays, network providers, and nearby radios can still observe operational metadata outside the project's logging layer.
|
|
|
|
## Privacy Manifests
|
|
|
|
`bitchat/PrivacyInfo.xcprivacy` declares:
|
|
|
|
- UserDefaults: `CA92.1` for app-only preferences and `1C8F.1` for the shared app group
|
|
- File timestamps/metadata: `C617.1` for app-container files and `3B52.1` for user-granted files
|
|
- System boot time: `35F9.1` for elapsed-time deadlines and timers
|
|
|
|
`bitchatShareExtension/PrivacyInfo.xcprivacy` declares app-group UserDefaults reason `1C8F.1`. Both manifests declare no tracking domains and no data collection by the app developer. They must remain bundled in their respective executable bundles.
|
|
|
|
## Panic Wipe Coverage
|
|
|
|
The panic action clears identity/session state, preferences, location state, groups, prekeys, outbox mail, courier mail, bridge dedup state, gossip archive, board data, managed media, and active subscriptions/transports. New persistent stores must add an explicit wipe hook and a regression test.
|
|
|
|
## Release Review Checklist
|
|
|
|
- Reconcile every new Application Support, UserDefaults, keychain, cache, or relay write with this assessment and `PRIVACY_POLICY.md`.
|
|
- Re-scan required-reason APIs and validate both bundled `PrivacyInfo.xcprivacy` files before archive submission.
|
|
- Verify panic wipe reaches any newly added persistent store.
|
|
- Treat geohash precision, bridge-cell changes, new relay tags, and announce fields as privacy-surface changes.
|
|
- Re-run real-device Bluetooth, background/locked-device recovery, location revocation, and audio-route checks; simulators cannot validate the physical side of those behaviors.
|