# How Bitchat Peer-to-Peer Communication Works: Architecture and Implementation

> Explore how Bitchat peer-to-peer communication achieves resilience using gossip synchronization, encrypted packets, relay routing, and deduplication for serverless messaging.

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

---

**Bitchat uses a gossip-style synchronization layer with encrypted packets, relay routing, and deduplication to enable resilient peer-to-peer messaging without central servers.**

The open-source **Bitchat** protocol implements peer-to-peer (P2P) communication through a lightweight gossip architecture that routes encrypted messages through configurable relays. This article examines the Swift implementation in [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat) to explain how peer-to-peer communication works at the code level.

## Core Architecture Components

Bitchat's P2P stack consists of three interconnected layers, each handling specific responsibilities in the communication pipeline.

| Component | Role | Key Source Files |
|-----------|------|----------------|
| **BitchatPeer** | Immutable peer identifier with public keys and routing metadata | [`bitchat/Models/BitchatPeer.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Models/BitchatPeer.swift) |
| **Sync Layer** | Packet encoding/decoding, rate-limiting, TTL handling | [`bitchat/Sync/RequestSyncPacket.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/RequestSyncPacket.swift), [`SyncResponseRateLimiter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/SyncResponseRateLimiter.swift), [`SyncTypeFlags.swift`](https://github.com/permissionlesstech/bitchat/blob/main/SyncTypeFlags.swift) |
| **GossipSyncManager** | Orchestrates when, what, and through which relay to synchronize | [`bitchat/Sync/GossipSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/GossipSyncManager.swift), [`RequestSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/RequestSyncManager.swift) |

### BitchatPeer: Identity Foundation

The `BitchatPeer` value type uniquely identifies each participant in the network. When a user logs in, this instance is created from the device's persistent private key in [`bitchat/Identity/SecureIdentityStateManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Identity/SecureIdentityStateManager.swift).

- **peerID**: A hex string that may carry prefixes (`mesh`, `noise`, `geoDM`) indicating the transport origin
- **Public keys**: Used for Noise protocol handshake and session establishment
- **Serialization helpers**: For network transmission and storage

## The Peer-to-Peer Communication Flow

### Step 1: Message Creation and Encryption

Outbound messages follow a strict encapsulation pipeline:

1. Plain text is wrapped in `BitchatMessage`
2. Payload is encrypted using **Noise** (XChaCha20-Poly1305)
3. Encrypted data is placed into a `RequestSyncPacket`

The `RequestSyncPacket` structure in [`bitchat/Models/RequestSyncPacket.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Models/RequestSyncPacket.swift) contains:

| Field | Purpose |
|-------|---------|
| `p` | Packet sequence number |
| `m` | Maximum fragment size |
| `data` | Encrypted payload |
| `types` | Bit-mask from `SyncTypeFlags` categorizing content |
| TTL | Time-to-live for relay forwarding |

```swift
// Create a sync packet for a new public message
let message = BitchatMessage(text: "Hello world!", from: myPeerID)
let encrypted = try message.encrypt(using: mySessionKey)  // Noise encryption

let packet = RequestSyncPacket(
    p: nextPacketSeq(),
    m: 1024,
    data: encrypted,
    types: .publicMessages
)

```

### Step 2: Gossip-Based Dispatch

`GossipSyncManager` in [`bitchat/Sync/GossipSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/GossipSyncManager.swift) controls packet distribution:

- Obtains reachable relays from `RelayController`
- Applies **relay jitter** to prevent traffic stampedes
- Invokes `RelayController.sendEvent(...)` for transmission
- Supports **multi-hop forwarding** through relay networks

```swift
let gossip = GossipSyncManager(
    myPeerID: myPeerID,
    config: .default,
    requestSyncManager: RequestSyncManager()
)
await gossip.send(packet: packet)

```

### Step 3: Relay Handling and Store-and-Forward

Relays treat packets as opaque binary blobs, enabling **store-and-forward** routing:

1. Relay receives packet and decrements TTL
2. If TTL > 0, forwards to connected peers
3. Original sender identity remains hidden from recipients

This design ensures message delivery even with intermittent connectivity and NAT-restricted networks.

### Step 4: Reception and Deduplication

Incoming packets traverse the `BLEReceivePipeline` or WebSocket-based Nostr transport:

```swift
func handleIncoming(_ raw: Data) async throws {
    let sync = try RequestSyncPacket.decode(from: raw)
    
    // Deduplication check using PacketIdUtil
    guard !gossip.isDuplicate(sync) else { return }
    
    let payload = try sync.decrypt(using: mySessionKey)
    let message = try BitchatMessage(payload)
    await chatService.deliver(message)
}

```

**Deduplication** occurs via `PacketIdUtil` in [`bitchat/Sync/PacketIdUtil.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/PacketIdUtil.swift), ensuring each logical message processes once even when arriving through multiple relays.

### Step 5: Rate Limiting and Abuse Protection

`SyncResponseRateLimiter` in [`bitchat/Sync/SyncResponseRateLimiter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/SyncResponseRateLimiter.swift) enforces:

- **Default limit**: 2 sync responses per 30-second window
- Per-peer throttling to constrain bandwidth
- Protection against spam and denial-of-service attacks

## Security Mechanisms in Peer-to-Peer Communication

Bitchat implements multiple security layers to protect message integrity and confidentiality:

- **End-to-end encryption**: All payloads use per-peer Noise session keys, ensuring confidentiality even across untrusted public relays
- **TTL enforcement**: Prevents infinite forwarding loops in the gossip network
- **Fragment filtering**: `RequestSyncPacket.encodeFragmentIdFilter` caps fragments per sync, blocking large-payload DoS attempts
- **Relay jitter**: Distributes traffic temporally to prevent congestion spikes

## Key Files for Understanding P2P Communication

| File | Purpose |
|------|---------|
| [`bitchat/Models/BitchatPeer.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Models/BitchatPeer.swift) | Peer identity and cryptographic helpers |
| [`bitchat/Models/RequestSyncPacket.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Models/RequestSyncPacket.swift) | Binary packet format with encryption, TTL, type flags |
| [`bitchat/Sync/SyncTypeFlags.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/SyncTypeFlags.swift) | Bit-mask enumeration for payload categories |
| [`bitchat/Sync/SyncResponseRateLimiter.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/SyncResponseRateLimiter.swift) | Per-window rate limiting implementation |
| [`bitchat/Sync/GossipSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/GossipSyncManager.swift) | High-level sync orchestration and relay coordination |
| [`bitchat/Sync/RequestSyncManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/RequestSyncManager.swift) | Low-level packet construction and validation |
| [`bitchat/Sync/PacketIdUtil.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/PacketIdUtil.swift) | Packet ID generation and deduplication logic |
| [`bitchat/Identity/SecureIdentityStateManager.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Identity/SecureIdentityStateManager.swift) | Persistent cryptographic identity storage |

## Summary

- **Bitchat's peer-to-peer communication** relies on gossip synchronization rather than direct peer connections, enabling operation through intermittent connectivity
- **Three-layer architecture**: `BitchatPeer` for identity, sync layer for packet handling, and `GossipSyncManager` for orchestration
- **Noise encryption** provides end-to-end security without trusting relays
- **Store-and-forward relays** with TTL and deduplication ensure message delivery while preserving sender anonymity
- **Rate limiting and fragment filtering** protect against abuse and bandwidth exhaustion

## Frequently Asked Questions

### How does Bitchat find peers without a central server?

Bitchat uses **configurable relays** discovered through `RelayController`. Peers do not need direct IP visibility—messages propagate through gossip-style forwarding where each relay forwards to its connected peers. The `GossipSyncManager` manages this process with jitter and TTL controls.

### What encryption protects messages in transit?

All payloads use **Noise protocol** with XChaCha20-Poly1305 authenticated encryption. Session keys are established per peer pair, ensuring that even relays handling the encrypted `RequestSyncPacket` cannot read message contents.

### How does Bitchat prevent the same message from processing multiple times?

The `PacketIdUtil` in [`bitchat/Sync/PacketIdUtil.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Sync/PacketIdUtil.swift) generates unique packet identifiers. `GossipSyncManager.isDuplicate()` checks against a deduplication cache before processing. This handles common scenarios where packets arrive via multiple relay paths.

### What limits message forwarding to prevent network floods?

Three mechanisms work together: **TTL decrements** at each hop prevent infinite forwarding, **relay jitter** spreads traffic temporally, and `SyncResponseRateLimiter` enforces a default maximum of 2 responses per 30 seconds per peer. Additionally, `encodeFragmentIdFilter` caps fragment counts per packet.