# How BLEPeerRegistry in BitChat Tracks and Manages Discovered Peers

> Discover how BitChat's BLEPeerRegistry efficiently tracks and manages discovered peers using in-memory dictionaries, crypto key pinning, and automatic pruning for optimal performance.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: deep-dive
- Published: 2026-07-27

---

**BitChat's `BLEPeerRegistry` maintains a lightweight in-memory dictionary of BLE peers, providing atomic upserts, cryptographic key pinning, connection state tracking, and automatic pruning of stale entries based on configurable retention rules.**

The `BLEPeerRegistry` struct in the [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat) repository serves as the authoritative ledger for Bluetooth Low Energy peer discovery. It tracks each node's identity, cryptographic keys, capabilities, and reachability status, enabling the mesh network to maintain an accurate real-time view of nearby devices without persistent database overhead.

## Core Data Structure and Storage

At the heart of the registry lies a type-safe dictionary that maps peer identifiers to comprehensive metadata records. In [`bitchat/Services/BLE/BLEPeerRegistry.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEPeerRegistry.swift) (lines 42-43), the storage is declared as:

```swift
private var peers: [PeerID: BLEPeerInfo] = [:]

```

Each `BLEPeerInfo` value captures critical peer attributes including the **nickname**, **Noise protocol public key**, **signing public key**, **connection status**, **last-seen timestamp**, **capabilities** (such as bridge mode), and optional **bridge geohash** for location-based rendezvous.

The registry exposes basic query helpers (lines 44-71) for common operations:

- `isEmpty` and `count` for inventory statistics
- `connectedCount` for active link metrics  
- `snapshotByID` for immutable peer views
- `nickname(for:connectedOnly:)` for UI display name resolution

## Adding and Updating Peer Records

The registry provides two distinct pathways for ingesting peer announcements, handling both unverified discovery and cryptographically verified identity assertions.

### Standard Upsert Operations

For basic peer discovery, the `upsert(_:)` method (lines 80-88) provides idempotent insertion:

```swift
mutating func upsert(_ info: BLEPeerInfo)

```

This overwrites existing entries entirely, making it suitable for initial discovery when processing raw BLE advertisements in [`BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEService.swift) (line 3578).

### Verified Announce with Trust-on-First-Use (TOFU)

When a peer presents a cryptographically signed announcement, `upsertVerifiedAnnounce` (lines 92-103) performs sophisticated key management:

```swift
mutating func upsertVerifiedAnnounce(
    peerID: PeerID,
    nickname: String,
    noisePublicKey: Data,
    signingPublicKey: Data,
    isConnected: Bool,
    now: Date,
    capabilities: Set<PeerCapability>,
    bridgeGeohash: String?
) -> BLEPeerAnnounceUpdate?

```

This method implements **TOFU (Trust on First Use)** semantics by preserving existing signing keys if present, preventing key downgrade attacks while allowing legitimate metadata updates. It returns an optional `BLEPeerAnnounceUpdate` struct indicating whether the peer is newly discovered or updated.

## Connection State Management

The registry isolates connection lifecycle events from metadata updates through dedicated atomic toggles. In [`BLEPeerRegistry.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEPeerRegistry.swift) (lines 62-76), state transitions are handled by:

```swift
mutating func markConnected(_ peerID: PeerID)
mutating func markDisconnected(_ peerID: PeerID)

```

These methods modify only the `isConnected` boolean within the stored `BLEPeerInfo`, leaving cryptographic material and capability advertisements intact. This separation allows the transport layer to rapidly fluctuate connection states without triggering full record re-validation or redundant cryptographic checks.

## Reachability and Automatic Pruning

To prevent stale peer accumulation in long-running mesh sessions, the registry implements differentiated time-based retention policies.

### Reachability Validation

The `isReachable(_:now:)` method (lines 93-104) evaluates timeout thresholds based on verification status:

- **Verified nicknames**: Extended retention periods for cryptographically trusted peers
- **Unverified nicknames**: Aggressive timeouts for anonymous devices

### Bulk Reconciliation

The `reconcileConnectivity(now:linkStates:)` method (lines 141-170) performs periodic garbage collection:

```swift
mutating func reconcileConnectivity(
    now: Date,
    linkStates: [PeerID: LinkState]
) -> BLEPeerRegistryChanges

```

This function compares registry contents against provided link states, disconnecting peers that exceed their time-to-live thresholds and removing entries that have become permanently unreachable. It returns a `BLEPeerRegistryChanges` structure detailing which peers were disconnected or purged, which `BLEService` uses to emit appropriate delegate callbacks.

## Capability Discovery and Bridge Rendezvous

Beyond basic presence tracking, the registry indexes peer capabilities to support intelligent mesh routing decisions.

### Capability-Based Filtering

The `peers(advertising:)` method (lines 24-28) returns all peers advertising specific capabilities:

```swift
func peers(advertising capability: PeerCapability) -> [BLEPeerInfo]

```

This enables the protocol to locate bridge nodes (`.bridge` capability) that connect the BLE mesh to internet-connected infrastructure.

### Geographic Bridge Discovery

For location-aware mesh joining, `advertisedBridgeGeohash()` (lines 29-36) extracts the first known bridge-geohash from capability-advertising peers:

```swift
func advertisedBridgeGeohash() -> String?

```

This allows GPS-less devices to discover geographic anchoring points when entering the mesh by referencing nearby bridge nodes that have advertised geohash coordinates.

## Integration with BLEService and Message Handlers

The registry serves as the ground truth for several higher-level BLE services through snapshot-based decoupling.

[`BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEService.swift) maintains a private `peerRegistry` instance and updates it when processing incoming announce packets or connection state changes. Rather than exposing mutable state directly, handlers such as [`BLEPublicMessageHandler.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEPublicMessageHandler.swift) (lines 18-19) receive a closure:

```swift
peersSnapshot: () -> [PeerID: BLEPeerInfo]

```

This pattern allows message handlers to resolve sender nicknames using current registry state without direct coupling to the mutable registry instance. Similarly, [`BLEPeerSenderDisplayName.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEPeerSenderDisplayName.swift) (lines 4-11) consumes these snapshots to provide human-readable identifiers, falling back to anonymous identifiers when peers lack verified nicknames.

## Practical Implementation Examples

The following patterns demonstrate typical registry interactions within BitChat's architecture:

```swift
// Initialize empty registry
var registry = BLEPeerRegistry()

// Record unverified discovery from BLE scan
let discovery = BLEPeerInfo(
    peerID: peerUUID,
    nickname: "Alice",
    isConnected: false,
    noisePublicKey: noiseKey,
    signingPublicKey: nil,
    isVerifiedNickname: false,
    lastSeen: Date(),
    capabilities: [.bridge],
    capabilitiesWereExplicitlyAdvertised: true,
    bridgeGeohash: nil
)
registry.upsert(discovery)

// Process verified announce with cryptographic proof
if let update = registry.upsertVerifiedAnnounce(
    peerID: peerUUID,
    nickname: "Alice",
    noisePublicKey: noiseKey,
    signingPublicKey: signingKey,
    isConnected: true,
    now: Date(),
    capabilities: [.bridge],
    bridgeGeohash: "u4pruydqqvj") {
    
    if update.isNewPeer {
        print("New verified peer joined: \(update.nickname)")
    }
}

// Mark connection established after GATT negotiation
registry.markConnected(peerUUID)

// Periodic maintenance (called every few seconds by BLEService)
let changes = registry.reconcileConnectivity(
    now: Date(),
    linkStates: currentLinkStates
)

// Query for bridge-capable peers to route messages
let bridges = registry.peers(advertising: .bridge)

// Build transport snapshots for outgoing announce packets
let snapshots = registry.transportSnapshots(selfNickname: "MyNode")

```

## Summary

- **In-memory storage**: Uses a `[PeerID: BLEPeerInfo]` dictionary in [`BLEPeerRegistry.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEPeerRegistry.swift) to maintain O(1) peer lookups without database persistence overhead.
- **Safe updates**: Implements TOFU key pinning via `upsertVerifiedAnnounce` while allowing simple overwrites via `upsert` for unverified discovery.
- **Lifecycle management**: Separates connection state (`markConnected`/`markDisconnected`) from peer metadata, with automatic pruning via `reconcileConnectivity` based on verification status.
- **Capability indexing**: Supports mesh routing through `peers(advertising:)` and geographic bridge discovery via `advertisedBridgeGeohash()`.
- **Service integration**: Acts as the source of truth for `BLEService`, message handlers, and UI components through immutable snapshot patterns.

## Frequently Asked Questions

### How does BLEPeerRegistry prevent malicious key substitution attacks?

The `upsertVerifiedAnnounce` method implements **TOFU (Trust on First Use)** semantics as defined in [`BLEPeerRegistry.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEPeerRegistry.swift) (lines 92-103). When processing a verified announce, the method checks for existing signing keys in the registry. If a key already exists for that `PeerID`, it preserves the existing key rather than overwriting it, preventing attackers from substituting keys through new announcements while allowing the legitimate key holder to update other metadata such as capabilities or connection status.

### What distinguishes upsert from upsertVerifiedAnnounce?

`upsert(_:)` performs a simple dictionary insertion or replacement of the entire `BLEPeerInfo` record (lines 80-88), suitable for initial unverified discovery when processing raw BLE advertisements. In contrast, `upsertVerifiedAnnounce` performs cryptographic validation, preserves existing signing keys to prevent downgrade attacks, tracks verification status separately, and returns metadata about whether the peer is new or updated. Use `upsert` for raw BLE scans; use `upsertVerifiedAnnounce` only for cryptographically authenticated announcements.

### How does the registry prevent memory exhaustion from abandoned devices?

The `reconcileConnectivity(now:linkStates:)` method (lines 141-170) applies time-based retention policies that automatically disconnect and eventually remove stale entries. Verified peers (those with cryptographically confirmed nicknames) survive longer than unverified anonymous peers. `BLEService` invokes this method periodically to prune the mesh view of unreachable devices, returning a `BLEPeerRegistryChanges` structure that tracks exactly which peers were affected.

### Can BLEPeerRegistry filter peers by supported mesh capabilities?

Yes. The `peers(advertising:)` method (lines 24-28) filters the registry by `PeerCapability` values such as `.bridge`, returning only peers that explicitly advertised support for that capability. Additionally, `advertisedBridgeGeohash()` (lines 29-36) locates bridge peers with geographic anchoring data, enabling location-based mesh joining strategies without requiring GPS on all participating devices.