# How BitChat Synchronizes Public Messages Between Peers: Gossip Protocol Implementation

> Discover how BitChat synchronizes public messages between peers using a gossip protocol. Learn about REQUEST_SYNC packets, attribution windows, and message verification.

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

---

**BitChat uses a gossip-style synchronization protocol where GossipSyncManager periodically sends REQUEST_SYNC packets to peers, RequestSyncManager validates responses using a 30-second attribution window, and BLEPublicMessageHandler delivers public messages after timestamp or RSR flag verification.**

BitChat implements a decentralized mesh networking stack to keep public chat history consistent across all connected devices without centralized servers. Understanding how public messages are synchronized between peers requires examining the three-layer architecture that handles request orchestration, response validation, and packet delivery. According to the permissionlesstech/bitchat source code, this design prevents sync floods while allowing historic messages to propagate through the mesh.

## The Three-Layer Synchronization Architecture

The synchronization flow splits into distinct responsibilities across three logical layers. Each layer operates independently to ensure efficient, secure message propagation.

### Layer 1: Sync Orchestration with GossipSyncManager

The **GossipSyncManager** (located in [`bitchat/Sync/GossipSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/GossipSyncManager.swift)) initiates all synchronization requests. It iterates over every connected peer and constructs a `REQUEST_SYNC` packet containing the `SyncTypeFlags.publicMessages` flag.

For normal synchronization, the packet includes `IS_RSR = 0x10`. For solicited responses, the packet sets `ttl = 0` alongside the RSR flag. Before transmission, the manager registers the outbound request via `RequestSyncManager.registerRequest(to: peerID)` to establish attribution tracking.

### Layer 2: Request Tracking via RequestSyncManager

The **RequestSyncManager** (in [`bitchat/Sync/RequestSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/RequestSyncManager.swift)) maintains a short-lived map of `peerID → requestTimestamp` entries with a default expiration window of **30 seconds**. This component prevents unsolicited sync floods by ensuring only valid responses are accepted.

When a response arrives, the handler calls `isValidResponse(from:isRSR:)`, which returns `true` only if the packet is marked as a Request-Sync-Response (RSR) **and** a matching pending request exists within the time window. Expired entries are automatically pruned by the `cleanup()` method.

### Layer 3: Packet Validation in BLEPublicMessageHandler

The **BLEPublicMessageHandler** (in [`bitchat/Services/BLE/BLEPublicMessageHandler.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEPublicMessageHandler.swift)) performs low-level verification before delivering messages to the UI. When receiving a `BitchatPacket`, it checks the `isRSR` flag:

- If `isRSR` is true, it validates the response against `RequestSyncManager` and skips the timestamp check to allow historic messages.
- If `isRSR` is false, it enforces the **2-minute timestamp rule** to reject stale or spoofed packets.

Valid packets are converted into `.publicMessageReceived` events and passed to the `PublicMessagePipeline` for local storage and UI notification.

## Public Message Packet Structure

The binary protocol encodes public message synchronization using specific flags and payload types defined in [`bitchat/Sync/SyncTypeFlags.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/SyncTypeFlags.swift) and [`bitchat/Noise/BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Noise/BinaryProtocol.swift).

- **Header**: Contains `SyncTypeFlags.publicMessages` to identify the sync type.
- **Payload**: Either an `AnnounceV2Packet` (for peer metadata) or a `BitchatMessage` with `type = .message`.
- **Flags**: The `IS_RSR` flag (value `0x10`) marks packets as responses to sync requests, exempting them from normal timestamp validation.

The [`BinaryProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BinaryProtocol.swift) encoder handles the new `IS_RSR` bit, ensuring responses are properly distinguished from unsolicited broadcasts.

## The Synchronization Flow Step-by-Step

The complete process of synchronizing public messages between peers follows this strict sequence:

1. **Periodic Sync Task**: `GossipSyncManager` builds a `REQUEST_SYNC` packet targeting `SyncTypeFlags.publicMessages`.
2. **Request Registration**: Before sending, it registers the request with `RequestSyncManager` to create the attribution window.
3. **Unicast Transmission**: The packet is sent to each peer individually—no blind broadcast storms occur.
4. **Peer Reception**: The recipient's `BLEService` decodes the packet via `BinaryProtocol`, sees `isRSR = false`, and applies the 2-minute timestamp rule.
5. **Response Generation**: The peer constructs a response packet with `isRSR = true` and `ttl = 0`.
6. **Attribution Check**: The original requester receives the packet; `BLEPublicMessageHandler` detects `isRSR = true` and queries `RequestSyncManager.isValidResponse(...)`. If pending, the timestamp check is bypassed.
7. **Delivery**: Valid payloads reach `PublicMessagePipeline` for archiving, emitting `.publicMessageReceived` events that `ChatViewModel` displays in the UI.

This flow is rate-limited to **8 responses per 30 seconds per peer** to prevent network congestion.

## Security and Efficiency Guarantees

The implementation provides three critical protections:

- **Attribution**: Only responses matching a pending request in `RequestSyncManager` are accepted, preventing unsolicited sync floods from malicious or malfunctioning peers.
- **Security**: Normal packets must fall within a 2-minute clock window; RSR packets bypass this only when cryptographically linked to a valid request.
- **Efficiency**: Synchronization uses unicast messaging rather than broadcast storms, with built-in rate limiting to preserve battery and bandwidth on mobile devices.

## Code Implementation Examples

The following Swift patterns demonstrate how the public message synchronization API is used throughout the codebase:

```swift
// Sending a public message from the UI layer
let viewModel = ChatViewModel(...)
viewModel.sendPublicMessage(
    content: "Hello, mesh!",
    to: .mesh          // public channel
)

// Internal pipeline queuing
publicMessagePipeline.enqueue(message, to: .mesh)

```

The BLE layer handles reception through the `BLEPublicMessageHandler`:

```swift
// Simplified validation logic in BLEPublicMessageHandler.swift
publicMessageHandler.handle(packet, from: peerID)

if packet.isRSR {
    // Verify the response belongs to a pending request
    guard requestSyncManager.isValidResponse(from: peerID, isRSR: true) else { return }
    // Accept historic messages beyond 2-minute window
} else {
    // Enforce timestamp freshness (2 minutes)
    guard abs(now - packet.timestamp) < 120 else { return }
}

// Deliver to UI
delegate.publicMessageReceived(
    peerID: peerID,
    nickname: packet.nickname,
    content: packet.content,
    timestamp: packet.timestamp,
    messageID: packet.messageID
)

```

## Summary

- **GossipSyncManager** initiates sync by sending `REQUEST_SYNC` packets with `SyncTypeFlags.publicMessages` and registering requests in `RequestSyncManager`.
- **RequestSyncManager** maintains a 30-second attribution window to validate RSR-flagged responses and prevent unsolicited sync floods.
- **BLEPublicMessageHandler** enforces a 2-minute timestamp rule for normal packets while bypassing checks for valid RSR responses.
- The protocol uses `IS_RSR = 0x10` flagging and `ttl = 0` to distinguish solicited historic messages from live traffic.
- Rate limiting (8 responses per 30 seconds per peer) and unicast transmission prevent mesh network congestion.

## Frequently Asked Questions

### What is the IS_RSR flag in BitChat's synchronization protocol?

The **IS_RSR** (Request-Sync-Response) flag is a binary bitmask value (`0x10`) set in the packet header to indicate that a message is a response to a specific synchronization request rather than a live broadcast. When `isRSR` is true, the receiving peer queries `RequestSyncManager` to verify the response matches a pending request, allowing the packet to bypass the standard 2-minute timestamp validation and accept older historic messages.

### How does BitChat prevent stale or spoofed messages during synchronization?

BitChat enforces a **2-minute timestamp window** on all incoming packets that are not marked as RSR responses, rejecting any packet where `abs(now - packet.timestamp) >= 120`. Additionally, the `RequestSyncManager` requires that RSR-flagged packets match a previously registered request within a 30-second window, ensuring only solicited historic data is accepted and preventing replay attacks or unsolicited sync floods.

### What is the time window for tracking sync requests in RequestSyncManager?

The default tracking window is **30 seconds**. When `GossipSyncManager` sends a `REQUEST_SYNC` packet, it registers the peer ID and timestamp in `RequestSyncManager`. The `isValidResponse(from:isRSR:)` method only returns true if the incoming RSR packet arrives within this window, after which `cleanup()` prunes expired entries to prevent memory growth.

### How does BitChat secure public message synchronization against spoofing?

The protocol ensures **attribution** by requiring all historic messages (RSR packets) to correlate with a registered request in `RequestSyncManager`, preventing arbitrary injection of old messages. Combined with the 2-minute freshness check for non-RSR packets and unicast-only sync transmission, these mechanisms ensure that peers only accept messages from legitimate mesh participants that are currently connected and responsive.