# BLEOutboundWriteBuffer: How bitchat Handles BLE Back-Pressure with Prioritized Write Queues

> Discover how bitchat's BLEOutboundWriteBuffer manages BLE backpressure with prioritized queues. It automatically discards low-priority packets to optimize data flow and prevent buffer overflow.

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

---

**The `BLEOutboundWriteBuffer` is a per-peripheral write queue in bitchat that enforces back-pressure through byte-capacity trimming, automatically discarding lowest-priority packets when queued data exceeds a configurable limit.**

The `BLEOutboundWriteBuffer` sits at the heart of bitchat's Bluetooth Low Energy (BLE) stack, isolating the hot path of outbound traffic from fragmentation and link-scheduling logic. Implemented in [`bitchat/Services/BLE/BLEOutboundWriteBuffer.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/BLE/BLEOutboundWriteBuffer.swift), this component ensures that high-priority control frames survive while protecting the BLE stack from memory pressure during heavy file transfers or flaky connections.

## How the Buffer Handles Outbound BLE Writes

The buffer maintains a dedicated write queue for each peripheral, keyed by UUID string. When your application calls `enqueue(data:for:priority:capBytes:)`, the buffer performs three critical operations in sequence.

### Step 1: Priority-Based Insertion

Each write receives a `BLEOutboundWritePriority` that combines a priority level (`high`, `fragmentBased`, `fileTransfer`, `low`) with a monotonic `suborder` counter. The buffer finds the correct insertion index using `firstIndex { item.priority < $0.priority }`, ensuring that urgent packets jump ahead of bulk data.

### Step 2: Byte-Cap Enforcement and Back-Pressure

After insertion, the buffer sums total queued bytes for that peripheral. While this sum exceeds `capBytes`, it repeatedly removes items from the tail—the lowest-priority writes—reporting `trimmedBytes` for each eviction. This **capacity-driven trimming** is the core back-pressure mechanism.

### Step 3: Caller Feedback via EnqueueResult

The method returns an `EnqueueResult` enum with two cases:

- `.enqueued(trimmed: Int, remaining: Int)` — the write survived, possibly after trimming lower-priority items
- `.oversized(Int)` — the payload itself exceeded `capBytes` and was rejected outright

The accompanying `accepted` Boolean tells you whether your specific write made it into the queue.

## Core API and Usage

```swift
import Foundation

// Initialize the buffer (normally owned by BLEService)
var writeBuffer = BLEOutboundWriteBuffer()

// Target peripheral
let peripheralID = "A1B2C3-D4E5-F6G7-H8I9-J0K1L2M3N4O5"

// Enqueue a high-priority keep-alive with 1 KB cap
let heartbeat = Data([0x01, 0x02, 0x03])
let result = writeBuffer.enqueue(
    data: heartbeat,
    for: peripheralID,
    priority: .high,
    capBytes: 1024
)

// Handle the result
switch result {
case .enqueued(let trimmed, let remaining):
    if trimmed > 0 {
        print("Trimmed \(trimmed) bytes of lower-priority writes")
    }
    print("Buffer now holds \(remaining) bytes for this peripheral")
case .oversized(let bytes):
    // Payload too large for cap—fragment before retrying
    print("Rejected: \(bytes)-byte payload exceeds \(1024)-byte cap")
}

// When CBPeripheral is ready to write...
let pending = writeBuffer.takeAll(for: peripheralID)
for write in pending {
    // write.payload → CBCharacteristic
}

```

## Lifecycle and Peripheral State Management

The buffer provides three helper methods that `BLEService` uses during connection churn:

| Method | Purpose |
| --- | --- |
| `takeAll(for:)` | Retrieves and clears all queued writes for a peripheral—called when the link becomes ready to transmit |
| `discardAll(for:)` | Drops all pending writes without delivery—used on disconnection |
| `prepend(_:for:)` | Re-inserts writes at queue head—useful when a peripheral reconnects mid-transfer |

These operations maintain the invariant that no stale writes accumulate for disconnected peripherals.

## Priority Level Semantics

Defined in [`BLEOutboundWritePriority.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEOutboundWritePriority.swift), the four levels map to real traffic types in bitchat:

1. **`.high`** — Control frames, keep-alives, acknowledgment packets
2. **`.fragmentBased`** — Chunks of an active message being reassembled by peer
3. **`.fileTransfer`** — Bulk payload data where some loss is tolerable
4. **`.low`** — Background sync, non-urgent gossip

When back-pressure strikes, `.low` items vanish first, followed by `.fileTransfer`, preserving in-flight fragments and control traffic.

## Testing the Back-Pressure Behavior

The test suite in [`bitchatTests/Services/BLEOutboundWriteBufferTests.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchatTests/Services/BLEOutboundWriteBufferTests.swift) verifies three invariants:

- Enqueue respects priority ordering across all insertion sequences
- Trimming never exceeds `capBytes` and always targets lowest-priority items
- The `accepted` flag and `EnqueueResult` values are internally consistent

Unit tests run against the buffer in isolation, without requiring actual `CBPeripheral` instances.

## Integration with BLEService

In [`BLEService.swift`](https://github.com/permissionlesstech/bitchat/blob/main/BLEService.swift), the buffer lives as `pendingPeripheralWrites: BLEOutboundWriteBuffer`. The `writeOrEnqueue(peripheral:data:priority:)` method bridges Core Bluetooth's asynchronous readiness callbacks with the buffer's synchronous API. When `CBPeripheralManager` signals a write completion, the service pulls the next batch via `takeAll(for:)` and streams it to the characteristic.

This separation—buffer for policy, service for I/O—allows independent testing and tuning of back-pressure parameters without touching BLE stack code.

## Summary

- **`BLEOutboundWriteBuffer`** provides per-peripheral, priority-ordered write queuing with automatic back-pressure enforcement
- **Back-pressure** works via byte-cap trimming: excess lowest-priority writes are discarded after each enqueue
- **Four priority levels** ensure critical control traffic survives congestion
- **`EnqueueResult`** gives explicit feedback on acceptance, trimming, and remaining capacity
- **Lifecycle helpers** (`takeAll`, `discardAll`, `prepend`) integrate cleanly with Core Bluetooth connection state

## Frequently Asked Questions

### What happens if a single payload exceeds the byte cap?

The buffer returns `.oversized(bytes)` without insertion. The write never enters the queue—no trimming occurs because the payload itself violates the capacity constraint. Fragment large payloads before enqueueing.

### How does priority interact with FIFO ordering within the same level?

Items sharing a priority level order by `suborder`, a monotonic counter incremented on each enqueue. This preserves FIFO semantics among peers while allowing strict precedence across levels.

### Can multiple peripherals share buffer capacity?

No. The `capBytes` parameter applies per-peripheral, not globally. Each peripheral UUID maintains an independent budget, preventing a single chatty peer from starving others.

### Where is the back-pressure behavior documented in the codebase?

The architectural rationale appears in [`docs/ARCHITECTURE_V2.md`](https://github.com/permissionlesstech/bitchat/blob/main/docs/ARCHITECTURE_V2.md), which states that pending-write back-pressure now lives in `BLEOutboundWriteBuffer` as a focused, unit-tested component before deeper fragmentation and link-scheduler work.