# What Is the RequestSyncManager in BitChat and How Does It Manage Sync Requests?

> Discover the RequestSyncManager in BitChat and learn how it secures the Bluetooth-mesh gossip protocol by validating sync requests and preventing floods.

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

---

**The `RequestSyncManager` tracks outgoing peer-to-peer sync requests and validates incoming responses to prevent unsolicited sync floods in BitChat's Bluetooth-mesh gossip protocol.**

BitChat uses a dual-layer sync architecture: a Bluetooth-mesh gossip layer for local peer discovery and an internet-based Nostr fallback. The `RequestSyncManager` sits at the core of this system, ensuring that every "request-sync-response" (RSR) packet is **solicited, timestamped, and time-bound**. This article breaks down its implementation in the [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat) repository.

## Core Responsibilities of the RequestSyncManager

The `RequestSyncManager` class in [`/bitchat/Sync/RequestSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main//bitchat/Sync/RequestSyncManager.swift) fulfills four primary duties:

### Register Outgoing Requests

When `GossipSyncManager` unicasts a `REQUEST_SYNC` packet to a specific peer, it registers that request immediately.

```swift
// RequestSyncManager.swift#L32-L40
public func registerRequest(to peer: PeerID) {
    queue.async(flags: .barrier) {
        self.pendingRequests[peer] = Date()
    }
}

```

The `barrier` flag ensures thread-safe writes to the `pendingRequests` dictionary. Each entry maps a `PeerID` to the exact timestamp when the request was issued.

### Validate Incoming RSR Packets

Responses must carry the `IS_RSR` flag and arrive within the configurable response window (default 30 seconds).

```swift
// RequestSyncManager.swift#L45-L65
public func isValidResponse(from peer: PeerID, isRSR: Bool) -> Bool {
    guard isRSR else { return false }
    
    return queue.sync {
        guard let requestTime = pendingRequests[peer] else {
            return false  // No pending request for this peer
        }
        let elapsed = Date().timeIntervalSince(requestTime)
        return elapsed <= responseWindow
    }
}

```

Unsolicited or expired responses are rejected at the transport layer, mitigating spoofing and sync-flood attacks.

### Periodic Cleanup

Stale entries are purged to bound memory usage:

```swift
// RequestSyncManager.swift#L68-L80
public func cleanup() {
    queue.async(flags: .barrier) {
        let now = Date()
        self.pendingRequests = self.pendingRequests.filter { _, requestTime in
            now.timeIntervalSince(requestTime) <= self.responseWindow
        }
    }
}

```

### Debug Introspection

Monitor sync state during development:

```swift
// RequestSyncManager.swift#L83-L85
public var debugPendingRequestCount: Int {
    return queue.sync { pendingRequests.count }
}

```

## Integration with the Sync Stack

The `RequestSyncManager` does not operate in isolation. Its lifecycle is tightly coupled to `GossipSyncManager`:

1. **Request emission** – `GossipSyncManager` calls `registerRequest(to:)` immediately before unicast【/bitchat/Sync/GossipSyncManager.swift#L59-L62】

2. **Response authentication** – The BLE service queries `isValidResponse(from:isRSR:)` before processing any flagged packet

3. **Maintenance** – `GossipSyncManager.performPeriodicMaintenance` triggers `cleanup()` on a fixed interval【/bitchat/Sync/GossipSyncManager.swift#L71-L73】

This design converts blind broadcast sync into **attribution-aware, solicited exchange**—a critical security property documented in [`/docs/REQUEST_SYNC_MANAGER.md`](https://github.com/permissionlesstech/bitchat/blob/main//docs/REQUEST_SYNC_MANAGER.md)【/docs/REQUEST_SYNC_MANAGER.md#L9-L13】.

## Complete Usage Example

```swift
import Foundation

// 1️⃣ Initialize with default 30-second window
let syncManager = RequestSyncManager()

// 2️⃣ Register before sending REQUEST_SYNC
let targetPeer = PeerID(id: "a1b2c3d4e5f6...")
syncManager.registerRequest(to: targetPeer)

// 3️⃣ Validate incoming RSR packets
func handlePacket(_ packet: BitchatPacket, from peer: PeerID) {
    guard packet.isRSR else {
        processNormalPacket(packet)
        return
    }
    
    guard syncManager.isValidResponse(from: peer, isRSR: true) else {
        logSecurityEvent("Rejected unsolicited RSR from \(peer)")
        return
    }
    
    applySyncResponse(packet)
}

// 4️⃣ Periodic maintenance (called from GossipSyncManager)
syncManager.cleanup()
print("Debug: \(syncManager.debugPendingRequestCount) pending sync requests")

```

## Configuration Options

| Parameter | Default | Purpose |
|-----------|---------|---------|
| `responseWindow` | 30.0 seconds | Maximum acceptable latency for RSR packets |
| `cleanupInterval` | Inherited from `GossipSyncManager` | Frequency of stale entry purging |

## Summary

- **The `RequestSyncManager` transforms blind sync into solicited sync** by tracking requests and validating responses
- **Thread safety** is enforced via barrier-protected serial queues on all mutable state
- **Time-bound validation** (default 30s window) prevents replay attacks and stale data injection
- **Tight integration** with `GossipSyncManager` ensures consistent request-response pairing throughout the sync lifecycle

## Frequently Asked Questions

### What happens if an RSR arrives without a matching pending request?

The `isValidResponse` method returns `false`, causing the transport layer to drop the packet. This prevents peers from flooding the network with unsolicited historical data.

### Can the response window be customized?

Yes. Initialize `RequestSyncManager` with a custom `responseWindow` parameter. The default 30 seconds accommodates typical Bluetooth-mesh latency while remaining strict enough to exclude stale responses.

### Why use a barrier queue instead of a simple lock?

Swift's `DispatchQueue` with barrier flags provides read-heavy optimization: concurrent reads proceed safely, while writes serialize with exclusive access. This matches the `RequestSyncManager` read-heavy access pattern (many validation checks, fewer registrations).

### How does this differ from Nostr's native sync mechanism?

Nostr operates over internet relays with its own authentication. The `RequestSyncManager` specifically governs the **Bluetooth-mesh gossip layer**, where peer attribution is harder to establish and unsolicited broadcasts are more easily exploited.