Bitchat Android Privacy Features: No Accounts, No Identifiers, and End-to-End Encryption
Bitchat Android requires no email, phone number, or sign-up, using only locally stored cryptographic keys and Noise-protocol end-to-end encryption to protect every message.
Bitchat Android is designed from the ground up to eliminate persistent user identifiers. As implemented in the permissionlesstech/bitchat-android repository, the messaging app never asks for personal credentials, instead deriving every identity from device-local key pairs that are stored solely on the device. This architecture guarantees that the only persistent identifier is a random cryptographic public key with no link to real-world identity.
Anonymous Identities Without User Accounts
Bitchat Android never requests an email, phone number, username, or password. Every user is represented by a locally generated cryptographic key pair managed through NostrIdentity.kt and persisted securely by SecureIdentityStateManager.kt.
Ed25519 and Noise Key Generation
On first launch, the app creates a permanent Ed25519 signing key pair and a static Noise key pair. The NostrIdentity class in app/src/main/java/com/bitchat/android/nostr/NostrIdentity.kt handles derivation of the Nostr public key, which becomes the sole routing identifier.
// Generate a fresh anonymous identity (first run only)
val (privateKeyHex, publicKeyHex) = NostrCrypto.generateKeyPair()
val identity = NostrIdentity.fromPrivateKey(privateKeyHex)
AES-256-GCM Key Storage
The private keys never leave the device. SecureIdentityStateManager.kt stores them in Android’s EncryptedSharedPreferences using an AES-256-GCM master key built at lines 57–63 of the same file. No cloud backup or server-side storage is performed.
// Store the identity securely (handled automatically by SecureIdentityStateManager)
val identityMgr = SecureIdentityStateManager(context)
identityMgr.saveSigningKey(
privateKey = Hex.decode(privateKeyHex),
publicKey = Hex.decode(publicKeyHex)
)
End-to-End Encryption and Replay Protection
All mesh packets are wrapped in Noise-encrypted payloads. The SecurityManager.kt file in app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt processes handshakes, signs results with the device’s private keys, and guarantees that only intended participants can decrypt messages.
Noise Protocol Handshake
Every encrypted session relies on the Noise protocol for confidentiality and authentication. The sendPrivateMessage flow shown below encodes plaintext into a NoisePayload and encrypts it before routing.
// Encrypt a message with the Noise protocol before sending
suspend fun sendPrivateMessage(
recipientPubkey: String,
plaintext: String,
myNoiseKey: ByteArray
) {
val noisePayload = NoisePayload.encode(
type = NoisePayloadType.PRIVATE_MESSAGE,
data = plaintext.toByteArray()
)
val encrypted = MeshService.encryptPayload(noisePayload, recipientPubkey, myNoiseKey)
// MeshService takes care of routing the encrypted packet over BLE / Wi-Fi Aware
MeshService.sendPacket(encrypted)
}
The handshake output is cryptographically bound to the device’s key material, preventing unauthorized decryption even if traffic is intercepted.
Deduplication and Replay Attack Defense
The SecurityManager maintains a bounded cache of processed message IDs via the processedMessages set and the MAX_PROCESSED_MESSAGES constant. This cache rejects replayed packets and limits opportunities for traffic analysis.
Decentralized Mesh Transport
Bitchat Android does not rely on phone numbers, SIM cards, or carrier identifiers. The mesh layer operates over Bluetooth Low Energy, Wi-Fi Aware, and optional Tor connections using hardware-generated MAC addresses.
Bluetooth and Wi-Fi Aware Routing
The transport implementations in BluetoothMeshService.kt and WifiAwareMeshService.kt route packets directly between peers. Because these transports do not require cellular service or telephony permissions, they avoid linking communication to any carrier-provided identity.
Geohash-Based Anonymity
Conversations can be mapped to a geohash rather than a user-specific ID. The GeohashConversationRegistry.kt and GeohashRepository.kt files in app/src/main/java/com/bitchat/android/mesh/ manage this mapping, allowing users to communicate within a location bucket without exposing a persistent peer ID.
Local Privacy Controls
Privacy settings are enforced entirely on the device with no external service involvement.
Per-Pubkey Blocking
The UI layer maintains a local blocklist checked before any message is processed. In NostrDirectMessageHandler.kt at lines 75–78, the isGeohashUserBlocked method filters incoming direct messages based on the sender’s public key.
// Decrypt an incoming message (handled in NostrDirectMessageHandler)
private suspend fun processNoisePayload(
payload: NoisePayload,
// …
) {
// payload already decrypted by NostrProtocol.decryptPrivateMessage(...)
// Convert to UI model, update chat UI, etc.
}
No Telemetry or Data Collection
The repository contains no analytics SDKs, tracking libraries, or telemetry code. All logging is limited to internal debugging via Log.d, Log.w, and Log.e, and these logs never transmit off-device.
Summary
- No accounts required: Bitchat Android generates anonymous identities from local Ed25519 and Noise key pairs without asking for email, phone numbers, or passwords.
- Local key storage:
SecureIdentityStateManager.ktpersists keys in Android’sEncryptedSharedPreferencesunder AES-256-GCM; no cloud or server storage is used. - Noise encryption: Every packet is encrypted end-to-end via the Noise protocol inside
SecurityManager.kt, with handshakes signed by the device’s private keys. - Replay protection: A bounded
processedMessagescache inSecurityManager.ktprevents replay attacks and deduplication-based tracking. - Carrier-free transport: Mesh networking over Bluetooth Low Energy and Wi-Fi Aware avoids SIM-linked identifiers entirely.
- Geohash routing:
GeohashConversationRegistry.ktenables location-bucket conversations without persistent peer IDs. - Local blocking: The
isGeohashUserBlockedcheck inNostrDirectMessageHandler.ktlets users filter senders without external services. - Zero telemetry: The codebase includes no analytics or tracking libraries.
Frequently Asked Questions
Does Bitchat Android require a phone number or email to sign up?
No. The app never requests an email address, phone number, or any personal credential. Identity comes entirely from a locally generated cryptographic key pair created in NostrIdentity.kt and stored by SecureIdentityStateManager.kt.
How does Bitchat Android protect private keys at rest?
Private keys are stored in Android’s EncryptedSharedPreferences using an AES-256-GCM master key configured in SecureIdentityStateManager.kt. This keystore-backed storage ensures keys never leave the device and are not backed up to the cloud.
What encryption protocol does Bitchat Android use?
The app uses the Noise protocol for end-to-end encryption. SecurityManager.kt handles handshake processing, and every mesh payload is encrypted so that only the intended recipient can decrypt it. Handshake results are signed with the device’s private Ed25519 key to authenticate peers.
Can users block others without revealing their identity or involving a server?
Yes. Blocking is handled locally through the isGeohashUserBlocked method in NostrDirectMessageHandler.kt. The blocklist is applied on-device before a message reaches the UI, and it does not communicate with any external service.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →