How Identity Announcements Are Propagated in the Bitchat Android Mesh Network
Identity announcements in Bitchat Android are propagated through a four-stage pipeline: creation of signed IdentityAnnouncement objects, gossip-style broadcast over BLE and Wi‑Fi Aware, cryptographic verification via Ed25519 signatures, and state synchronization to UI layers through reactive flows.
The Bitchat Android mesh network uses identity announcements to distribute peer metadata—nicknames, Noise public keys, Ed25519 signing keys, and capability flags—across a decentralized network without centralized servers. This article examines the propagation mechanism as implemented in the permissionlesstech/bitchat-android repository, tracing how data flows from local device construction through multi-hop mesh broadcast to verified UI display.
How Identity Announcement Propagation Works in Bitchat
The propagation system splits responsibility across four distinct stages, each handled by dedicated components in the Kotlin source tree.
Stage 1: Creating the Identity Announcement
When a mesh service starts, MeshCore.kt (lines 835–856) constructs a local IdentityAnnouncement using the static factory method IdentityAnnouncement.forLocalPeer(). This method assembles:
- The current nickname (from
NicknameProvider) - The static Noise key pair for encrypted sessions
- The Ed25519 signing key for authentication
- Capability flags indicating supported features
The result is TLV-encoded and cached for repeated broadcasts. The IdentityAnnouncement.kt model defines the wire format, encoding, decoding, and signature attachment functions.
// MeshCore.kt (lines 835-856)
val announcement = IdentityAnnouncement.forLocalPeer(
nickname = currentNickname,
staticKey = noiseStaticKey,
signingKey = edSigningKey
)
val payload = buildAnnouncementPayload(announcement, nickname)
Stage 2: Broadcasting via Mesh Gossip
The gossip protocol ensures announcements reach all reachable peers, including those that join after initial transmission. UnifiedMeshService.kt orchestrates this through a repeating announcement job that periodically injects the payload into the outbound stream.
Lower-level transport implementations handle actual transmission:
- Bluetooth LE:
BluetoothMeshService.kt(lines 1253–1316) segments and sends gossip packets - Wi‑Fi Aware:
WifiAwareMeshService.kt(line 179) publishes the announcement as a service discovery payload
Every node that receives a gossip packet forwards it to neighbors, enabling multi-hop propagation across the mesh. The GossipSyncManager deduplicates by peer-ID and timestamp to prevent amplification loops.
Stage 3: Reception and Cryptographic Verification
Incoming packets route through MessageHandler.kt (lines 293–322), which delegates validation to AnnouncementIdentityValidator.kt. The verification flow:
- Decode the raw payload with
IdentityAnnouncement.decode() - Extract the embedded Ed25519 signing public key
- Verify the signature against the announcement body
// MessageHandler.kt (lines 293-322)
val announcement = AnnouncementIdentityValidator.verify(packet, peerID) { sig, data, key ->
// Ed25519 signature verification
}
Verification failures mark the announcement as unverified—the nickname may display with a warning, but no cryptographic trust is established.
Stage 4: State Persistence and UI Propagation
Verified announcements trigger a chain of state updates:
PeerManager.kt (lines 22–64, 122–165) mutates the PeerInfo registry:
peerManager.updatePeerInfoFromVerifiedAnnouncement(
peerID = peerID,
nickname = announcement.nickname,
noisePublicKey = announcement.noisePublicKey,
signingPublicKey = announcement.signingPublicKey,
isVerified = true,
capabilities = announcement.capabilities
)
SecureIdentityStateManager.kt persists the fingerprint derived from the Noise public key, while PeerFingerprintManager.kt maintains a global peer-ID → fingerprint cache.
UI layers observe changes through reactive state flows. On Wear OS, WearPeerIdentityState.kt exposes revision as a StateFlow and provides snapshot() for Compose consumption:
// WearPeerIdentityState.kt (line 51)
val identity = WearPeerIdentityState.snapshot(peerID, mesh)
Components like PeopleScreen and PeerDebugScreen recompose automatically when verification status or nicknames change.
Key Source Files for Identity Propagation
| File | Responsibility |
|---|---|
IdentityAnnouncement.kt |
TLV data model, encode/decode, signature handling |
MeshCore.kt |
Local announcement payload construction |
BluetoothMeshService.kt |
BLE gossip transmission |
WifiAwareMeshService.kt |
Wi‑Fi Aware announcement publishing |
UnifiedMeshService.kt |
Periodic broadcast orchestration |
AnnouncementIdentityValidator.kt |
Ed25519 signature verification |
MessageHandler.kt |
Packet routing to validation and storage |
PeerManager.kt |
Verified peer state management |
SecureIdentityStateManager.kt |
Fingerprint persistence |
PeerFingerprintManager.kt |
Global identity cache |
WearPeerIdentityState.kt |
Wear OS UI state bridge |
Summary
- Identity announcement propagation in Bitchat Android relies on gossip-style flooding with periodic retransmission to handle late-joining peers
- Cryptographic verification via Ed25519 ensures authenticity before any trust decisions are made
- Multi-transport support (BLE + Wi‑Fi Aware) maximizes mesh connectivity while abstracting differences through
UnifiedMeshService - Reactive UI architecture decouples identity state from presentation, supporting both phone and Wear OS surfaces
Frequently Asked Questions
How often does Bitchat rebroadcast identity announcements?
The UnifiedMeshService.announcementJob schedules periodic retransmission so newly discovered peers eventually receive identity data even if they missed the initial broadcast. The exact interval is configured in the service's coroutine scope but designed to balance mesh convergence against battery consumption.
What happens if an identity announcement signature fails verification?
AnnouncementIdentityValidator.verify() returns null on failure. MessageHandler treats the peer as unverified—the nickname may still display (with visual warning), but no cryptographic identity is established and capability flags are disregarded until a valid announcement arrives.
Can a peer change its nickname after joining the mesh?
Yes. A new IdentityAnnouncement with updated nickname and fresh signature can be issued at any time. The updated announcement propagates through the same gossip pipeline, and PeerManager.updatePeerInfoFromVerifiedAnnouncement() overwrites the stored nickname while preserving the verified status if the signing key matches previous announcements.
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 →