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:

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:

  1. Decode the raw payload with IdentityAnnouncement.decode()
  2. Extract the embedded Ed25519 signing public key
  3. 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →