This document describes the rate limiting mechanisms and quality-of-service (QoS) features implemented throughout the BitChat protocol stack. Rate limiting protects the mesh network from accidental and malicious traffic floods, prevents enumeration attacks, and ensures fair resource allocation. QoS features prioritize critical protocol messages (handshakes, acknowledgments) over bulk data transfers and maintain system stability under high load.
BitChat implements rate limiting at multiple layers, from low-level BLE packet pacing and sync request throttling to high-level Noise protocol handshake protection and Nostr Proof-of-Work (PoW) validation.
The system employs a layered approach to traffic control, utilizing token buckets, sliding window timestamp checks, and Proof-of-Work bypasses.
The following diagram bridges high-level rate limiting concepts to their specific implementations in the codebase.
Rate Limiting and QoS Components
Sources: bitchat/Services/RelayController.swift11-25 bitchat/Noise/SecureNoiseSession.swift11-37 bitchat/Noise/NoiseRateLimiter.swift13-79 bitchat/ViewModels/MessageRateLimiter.swift10-49 bitchat/Services/BLE/BLEIngressLinkRegistry.swift25-75
The NoiseRateLimiter bitchat/Noise/NoiseRateLimiter.swift13 provides defense-in-depth against handshake exhaustion and message flooding at the cryptographic layer. It maintains both per-peer and global counters using sliding time windows managed within a concurrent queue with barrier flags bitchat/Noise/NoiseRateLimiter.swift21-24
To prevent CPU exhaustion from expensive Curve25519 operations during handshakes, the system enforces limits via allowHandshake(from:) bitchat/Noise/NoiseRateLimiter.swift23:
maxHandshakesPerMinute (checked against handshakeTimestamps dictionary) bitchat/Noise/NoiseRateLimiter.swift39maxGlobalHandshakesPerMinute (checked against globalHandshakeTimestamps array) bitchat/Noise/NoiseRateLimiter.swift30Even after a session is established, traffic is monitored via allowMessage(from:) bitchat/Noise/NoiseRateLimiter.swift52:
maxMessagesPerSecond bitchat/Noise/NoiseRateLimiter.swift68maxGlobalMessagesPerSecond bitchat/Noise/NoiseRateLimiter.swift59Sources: bitchat/Noise/NoiseRateLimiter.swift13-96 bitchat/Noise/NoiseSecurityConstants.swift25-30
For public geohash channels, BitChat uses a dual token-bucket system in MessageRateLimiter bitchat/ViewModels/MessageRateLimiter.swift10
The MessageRateLimiter tracks senderBuckets and contentBuckets bitchat/ViewModels/MessageRateLimiter.swift31-32
BitChat implements NIP-13 PoW via the NostrPoW utility bitchat/Nostr/NostrPoW.swift12
nonce tag targeting targetBits (8 bits) bitchat/Nostr/NostrPoW.swift19validatedDifficulty function checks if the event ID hash meets the committed target bitchat/Nostr/NostrPoW.swift67-77rateLimitBypassBits (8 bits), the per-sender rate limit is skipped entirely bitchat/ViewModels/MessageRateLimiter.swift58-60 The content-flood bucket still applies to prevent PoW-funded duplicate spam bitchat/ViewModels/MessageRateLimiter.swift77-80Sources: bitchat/ViewModels/MessageRateLimiter.swift51-81 bitchat/Nostr/NostrPoW.swift19-77
The RelayController bitchat/Services/RelayController.swift11 centralizes flood control policy for the Bluetooth mesh. It makes a RelayDecision based on packet type, graph density, and TTL.
The controller dynamically adjusts the newTTL to contain floods while ensuring reachability:
degree >= highDegreeThreshold), these are clamped to bleFragmentRelayTtlCapDense to prevent sustained airtime exhaustion bitchat/Services/RelayController.swift54-60isRequestSync packets are never relayed, preventing a single peer from triggering network-wide gossip replays bitchat/Services/RelayController.swift30-32To avoid packet collisions in the mesh, RelayController applies random delays:
Sources: bitchat/Services/RelayController.swift11-97 bitchat/Services/BLE/BLEIngressLinkRegistry.swift112-117
The BLEIngressLinkRegistry bitchat/Services/BLE/BLEIngressLinkRegistry.swift25 manages the first line of defense for incoming packets.
Every packet is assigned a unique messageID derived from the sender, timestamp, type, and a payload hash prefix bitchat/Services/BLE/BLEIngressLinkRegistry.swift106-110 The registry uses recordIfNew to suppress duplicates within a configurable lifetime bitchat/Services/BLE/BLEIngressLinkRegistry.swift48-63
The BLEIngressPacketGuard evaluates ingress packets against security rules bitchat/Services/BLE/BLEIngressPacketGuard.swift15-23:
maxTimestampSkewMs window (default 120s) are dropped to prevent replay attacks bitchat/Services/BLE/BLEIngressPacketGuard.swift98-120boundPeerID for link-local operations like requestSync bitchat/Services/BLE/BLEIngressLinkRegistry.swift81-84Sources: bitchat/Services/BLE/BLEIngressLinkRegistry.swift25-126 bitchat/Services/BLE/BLEIngressPacketGuard.swift7-163
The SecureNoiseSession bitchat/Noise/SecureNoiseSession.swift11 enforces strict protocol bounds to prevent memory exhaustion and cryptographic wear-out.
| Limit Type | Property / Constant | Action on Breach |
|---|---|---|
| Message Limit | maxMessagesPerSession | Throws sessionExhausted bitchat/Noise/SecureNoiseSession.swift23-25 |
| Session Age | sessionTimeout | Throws sessionExpired bitchat/Noise/SecureNoiseSession.swift18-20 |
| Message Size | maxMessageSize | Throws messageTooLarge bitchat/Noise/SecureNoiseSession.swift28-30 |
| Renegotiation | needsRenegotiation() | Returns true at 90% usage or inactivity bitchat/Noise/SecureNoiseSession.swift56-69 |
Sources: bitchat/Noise/SecureNoiseSession.swift11-69 bitchat/Noise/NoiseSecurityConstants.swift11-31
| Category | Key | Value | File Reference |
|---|---|---|---|
| PoW | targetBits | 8 bits | bitchat/Nostr/NostrPoW.swift19 |
| PoW | miningTimeCap | 2.0s | bitchat/Nostr/NostrPoW.swift29 |
| Noise | maxHandshakesPerMinute | 10 | bitchat/Noise/NoiseSecurityConstants.swift25 |
| Noise | maxMessagesPerSecond | 100 | bitchat/Noise/NoiseSecurityConstants.swift26 |
| Mesh | bleFragmentRelayTtlCap | 4 | bitchat/Services/RelayController.swift59 |
| Mesh | bleHighDegreeThreshold | 6 | bitchat/Services/RelayController.swift25 |
Sources: bitchat/Services/RelayController.swift11-97 bitchat/Noise/NoiseSecurityConstants.swift11-31 bitchat/Nostr/NostrPoW.swift19-29
Refresh this wiki
This wiki was recently refreshed. Please wait 6 days to refresh again.