# How Identity Announcements Are Propagated in the Bitchat Android Mesh Network

> Learn how identity announcements propagate in the Bitchat Android mesh network through a four-stage pipeline: creation, broadcast, verification, and synchronization. Discover the technical details.

- Repository: [permissionlesstech/bitchat-android](https://github.com/permissionlesstech/bitchat-android)
- Tags: internals
- Published: 2026-08-04

---

**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](https://github.com/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`](https://github.com/permissionlesstech/bitchat-android/blob/main/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`](https://github.com/permissionlesstech/bitchat-android/blob/main/IdentityAnnouncement.kt) model defines the wire format, encoding, decoding, and signature attachment functions.

```kotlin
// 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`](https://github.com/permissionlesstech/bitchat-android/blob/main/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`](https://github.com/permissionlesstech/bitchat-android/blob/main/BluetoothMeshService.kt) (lines 1253–1316) segments and sends gossip packets
- **Wi‑Fi Aware**: [`WifiAwareMeshService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/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`](https://github.com/permissionlesstech/bitchat-android/blob/main/MessageHandler.kt) (lines 293–322), which delegates validation to [`AnnouncementIdentityValidator.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/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

```kotlin
// 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`](https://github.com/permissionlesstech/bitchat-android/blob/main/PeerManager.kt)** (lines 22–64, 122–165) mutates the `PeerInfo` registry:

```kotlin
peerManager.updatePeerInfoFromVerifiedAnnouncement(
    peerID = peerID,
    nickname = announcement.nickname,
    noisePublicKey = announcement.noisePublicKey,
    signingPublicKey = announcement.signingPublicKey,
    isVerified = true,
    capabilities = announcement.capabilities
)

```

**[`SecureIdentityStateManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/SecureIdentityStateManager.kt)** persists the **fingerprint** derived from the Noise public key, while [`PeerFingerprintManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/PeerFingerprintManager.kt) maintains a global peer-ID → fingerprint cache.

UI layers observe changes through **reactive state flows**. On Wear OS, [`WearPeerIdentityState.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/WearPeerIdentityState.kt) exposes `revision` as a `StateFlow` and provides `snapshot()` for Compose consumption:

```kotlin
// 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`](https://github.com/permissionlesstech/bitchat-android/blob/main/IdentityAnnouncement.kt) | TLV data model, encode/decode, signature handling |
| [`MeshCore.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/MeshCore.kt) | Local announcement payload construction |
| [`BluetoothMeshService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/BluetoothMeshService.kt) | BLE gossip transmission |
| [`WifiAwareMeshService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/WifiAwareMeshService.kt) | Wi‑Fi Aware announcement publishing |
| [`UnifiedMeshService.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/UnifiedMeshService.kt) | Periodic broadcast orchestration |
| [`AnnouncementIdentityValidator.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/AnnouncementIdentityValidator.kt) | Ed25519 signature verification |
| [`MessageHandler.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/MessageHandler.kt) | Packet routing to validation and storage |
| [`PeerManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/PeerManager.kt) | Verified peer state management |
| [`SecureIdentityStateManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/SecureIdentityStateManager.kt) | Fingerprint persistence |
| [`PeerFingerprintManager.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/PeerFingerprintManager.kt) | Global identity cache |
| [`WearPeerIdentityState.kt`](https://github.com/permissionlesstech/bitchat-android/blob/main/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.