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

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 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
Sync Layer Packet encoding/decoding, rate-limiting, TTL handling bitchat/Sync/RequestSyncPacket.swift, SyncResponseRateLimiter.swift, SyncTypeFlags.swift
GossipSyncManager Orchestrates when, what, and through which relay to synchronize bitchat/Sync/GossipSyncManager.swift, 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.

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

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, 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 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 Peer identity and cryptographic helpers
bitchat/Models/RequestSyncPacket.swift Binary packet format with encryption, TTL, type flags
bitchat/Sync/SyncTypeFlags.swift Bit-mask enumeration for payload categories
bitchat/Sync/SyncResponseRateLimiter.swift Per-window rate limiting implementation
bitchat/Sync/GossipSyncManager.swift High-level sync orchestration and relay coordination
bitchat/Sync/RequestSyncManager.swift Low-level packet construction and validation
bitchat/Sync/PacketIdUtil.swift Packet ID generation and deduplication logic
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 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.

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 →