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

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 repository.

Core Responsibilities of the RequestSyncManager

The RequestSyncManager class in /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.

// 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).

// 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:

// 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:

// 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【/docs/REQUEST_SYNC_MANAGER.md#L9-L13】.

Complete Usage Example

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.

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 →