Peer-to-Peer (P2P): Decentralized Architecture

Peer-to-Peer (P2P): Decentralized Architecture
Peer-to-peer (P2P) architecture eliminates the central server: every node in the network is both a client and a server. It requests resources from peers and simultaneously serves resources to other peers. This design was popularised by BitTorrent for file sharing, powers blockchain networks like Bitcoin and Ethereum, and underlies modern distributed storage systems like IPFS.
Understanding P2P architecture requires mastering the distributed data structures (DHTs), consensus mechanisms, and network protocols that replace the coordination role normally played by a central server.
Client-Server vs. Peer-to-Peer
Client-Server:
Client A ──► Server ◄── Client B
│
└── If server fails, all clients lose access
Peer-to-Peer:
Node A ◄──► Node B ◄──► Node C
│ │
└────────────────────────┘
If Node A goes offline, B and C continue serving each other| Property | Client-Server | Peer-to-Peer |
|---|---|---|
| Single point of failure | Yes (the server) | No |
| Scalability | Limited by server capacity | Scales with number of nodes |
| Latency | Consistent (fast server) | Variable (depends on peers) |
| Availability | Depends on server uptime | Increases with more peers |
| Bandwidth cost | Paid by operator | Distributed across all peers |
| Coordination | Centralised, simple | Distributed, complex |
| Privacy | Server knows all traffic | Variable |
| Censorship resistance | Low | High |
P2P sacrifices consistency and predictable performance for resilience, decentralisation, and scalability that is funded by the participants, not the operator.
Distributed Hash Tables (DHT)
In client-server, a directory service (DNS, a database) maps names to locations. In P2P, a Distributed Hash Table (DHT) provides this mapping without any central server.
How DHT works
Each node in the network is assigned a node ID (a random 160-bit number in BitTorrent's Kademlia DHT). Each piece of data is assigned a key (a hash of the content). The DHT stores key → location mappings distributed across nodes: node N stores data for keys numerically close to its own ID.
Node IDs: 001, 017, 042, 089, 156, 201, 240 (simplified to 8-bit)
↑
Key 044 is closest to node 042 → node 042 stores "which peer has this data"
Finding who has key 044:
1. You ask your nearest neighbour
2. They return the node closer to 044 than themselves
3. Repeat 4-5 hops until you reach the node responsible for key 044
4. That node tells you which peers currently have the dataLookup: "Who has chunk 044?"
You (ID=001) ──► Node 017 ──► Node 042 (responsible) ──► "Peer 156 has it"
4-5 hops maximum in a 1,000,000 node networkThis logarithmic lookup is why DHT-based P2P networks like BitTorrent can locate data in a network of a million nodes with just 20 messages.
BitTorrent DHT (Kademlia)
// Simplified lookup pseudocode (Kademlia)
function findNode(targetId) {
// Start with k closest known nodes to targetId
let candidates = routingTable.kClosest(targetId);
let visited = new Set();
while (true) {
// Query each candidate for their k closest to targetId
const newNodes = await Promise.all(
candidates.filter(n => !visited.has(n.id))
.map(n => n.findNode(targetId))
);
candidates.forEach(n => visited.add(n.id));
const allNodes = [...candidates, ...newNodes.flat()];
const nextBest = kClosest(allNodes, targetId);
if (JSON.stringify(nextBest) === JSON.stringify(candidates)) break;
candidates = nextBest;
}
return candidates; // k closest nodes to targetId
}Gossip Protocols
In a centralised system, when data changes (a new Bitcoin transaction), the server knows immediately. In P2P, information must spread without a central broadcaster. Gossip protocols solve this:
- Node A receives a new transaction
- Node A selects 3 random peers and tells them
- Each peer selects 3 random peers and tells them
- Each of those selects 3 more...
After just 20 rounds, a network of 1,000,000 nodes has received the message. The fan-out grows exponentially: 3^20 = 3.5 billion — more than enough to cover any realistic network.
// Gossip protocol implementation
class GossipNode {
private peers: Set<string> = new Set();
private seen: Set<string> = new Set(); // Message IDs already processed
private fanout = 3; // Number of peers to forward to per round
async gossip(message: { id: string; payload: unknown }) {
if (this.seen.has(message.id)) return; // Deduplicate
this.seen.add(message.id);
// Process the message locally
this.handleMessage(message.payload);
// Forward to random subset of peers
const targetPeers = [...this.peers]
.sort(() => Math.random() - 0.5)
.slice(0, this.fanout);
await Promise.all(
targetPeers.map(peer =>
this.sendToPeer(peer, message).catch(() => {}) // Ignore failed peers
)
);
}
private handleMessage(payload: unknown) {
console.log('Received:', payload);
}
private async sendToPeer(peerId: string, message: unknown): Promise<void> {
// Send over WebRTC, WebSocket, or custom protocol
}
}Gossip protocols are eventually consistent — every node will receive the message, but not at the exact same time. Bitcoin uses this to broadcast transactions across 10,000+ nodes in seconds.
Blockchain: P2P Consensus
Blockchain extends P2P with a consensus mechanism that enables nodes that do not trust each other to agree on a shared state (which transactions have occurred, in what order).
Block structure:
┌────────────────────────────────────â”
│ Block #1000 │
│ Previous hash: 0000abc... │
│ Merkle root: ff34de... │
│ Timestamp: 1713436800 │
│ Nonce: 38492817 │
│ Transactions: [tx1, tx2, tx3, ...] │
└────────────────────────────────────┘
│ SHA256(SHA256(block header))
â–¼
block hash: 00000xyz... ↠must start with N zeros (Proof of Work)
│
â–¼ becomes "Previous hash" in Block #1001Changing any historical block would change its hash, which changes the next block's hash, which cascades through the entire chain. Every node in the network would reject the tampered chain because it would not match their copy.
Merkle trees for efficient verification
Bitcoin uses Merkle trees to let lightweight clients verify that a specific transaction is in a block without downloading the entire block:
Transactions: [tx1, tx2, tx3, tx4]
Merkle Tree:
root = H(H12 + H34)
/ \
H12 = H(H1+H2) H34 = H(H3+H4)
/ \ / \
H1=H(tx1) H2=H(tx2) H3=H(tx3) H4=H(tx4)
To prove tx3 is in the block, only provide: [H4, H12, root]
Verifier computes: H(H(H(tx3) + H4) + H12) == root? → yes, tx3 is includedIPFS: P2P Web
IPFS (InterPlanetary File System) is a content-addressed P2P file system. Instead of URLs that identify location (https://server.com/file), IPFS uses content identifiers (CIDs) that identify content:
# Add a file to IPFS
ipfs add index.html
# Returns: QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
# The CID is a hash of the content
# Anyone with the CID can retrieve the file from any IPFS node that has it:
ipfs cat QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
# Access via HTTP gateway
curl https://ipfs.io/ipfs/QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdGKey property: the content can never change without the CID changing. There is no way to silently update a file at the same IPFS address — any change produces a different CID.
Hosting a static site on IPFS
# Build your site
npm run build
# Add the entire directory to IPFS
ipfs add -r ./dist
# Returns CID for the root directory
# Pin to Pinata or Fleek to ensure availability (not just on your node)
curl -X POST "https://api.pinata.cloud/pinning/pinByHash" \
-H "Authorization: Bearer $PINATA_JWT" \
-H "Content-Type: application/json" \
-d '{"hashToPin": "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG"}'WebRTC: Browser-to-Browser P2P
WebRTC enables direct peer-to-peer connections between browsers — video calls, screen sharing, and file transfer without a server handling the media:
// Simple WebRTC peer connection
const peerConnection = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' } // STUN server for NAT traversal
]
});
// Data channel for arbitrary data
const channel = peerConnection.createDataChannel('messages');
channel.onmessage = (event) => console.log('Received:', event.data);
// Create offer (initiating peer)
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
// Exchange offer/answer via a signalling server (small centralised component)
// Then the media flows directly peer-to-peerNote: WebRTC still requires a signalling server to exchange connection metadata (offer/answer/ICE candidates), but the actual media and data flows directly between peers. Video calls using WebRTC do not pass through a server — latency is dramatically lower than a server-relayed connection.
P2P Use Cases in Production Engineering
| Use case | P2P technology | Why P2P |
|---|---|---|
| Software update distribution | IPFS, BitTorrent (Windows Update) | Reduces CDN bandwidth cost |
| Video conferencing | WebRTC | Low latency direct connections |
| Decentralised storage | Filecoin, Storj, IPFS | Censorship-resistant, no central operator |
| Blockchain applications | Bitcoin, Ethereum network | Trustless consensus |
| Edge computing coordination | Gossip protocols | Self-organising networks |
| Mesh networking | libp2p | Resilient community networks |
Frequently Asked Questions
Q: Is P2P inherently slower than client-server?
For small networks of peers with slow connections, yes. For large, well-seeded networks, P2P can be faster — BitTorrent distributes the bandwidth cost across all seeders, so popular files (Linux ISOs, software releases) can be downloaded faster via torrent than from a single server. The trade-off is unpredictability: download speed depends on the health of the swarm.
Q: How does a new node join a P2P network?
Most P2P protocols use a bootstrap node list — a small set of well-known, stable nodes maintained by the network operators. The new node contacts a bootstrap node, which provides a list of active peers. From there, the DHT lookup mechanism takes over. Bitcoin hardcodes DNS seeds; Ethereum uses a list of bootnodes. Once the node has discovered enough peers, it no longer needs the bootstrap nodes.
Q: Is all P2P traffic illegal?
P2P is a network architecture, not a legal category. BitTorrent is the same protocol used for pirating movies and distributing Linux distributions, academic papers via Sci-Hub, and Microsoft Windows updates. The protocol is neutral. IPFS powers censorship-resistant archives of publicly important content. WebRTC powers Zoom. Legality depends entirely on the content, not the protocol.
Q: How does blockchain prevent the same token from being spent twice?
The double-spend problem is solved by the consensus mechanism. All transactions are broadcast to all nodes via gossip. Miners/validators collect transactions and include them in blocks. Once a transaction is included in a confirmed block (6+ blocks deep in Bitcoin), the network's consensus makes it computationally infeasible to revert — it would require recomputing more proof-of-work than the honest majority of the network's combined hash power.
Key Takeaway
P2P architecture replaces the central server with a distributed network where every node contributes to the shared resource. DHTs enable decentralised lookup without a directory server. Gossip protocols propagate information to all nodes exponentially fast. Blockchain extends these with consensus to create tamper-evident shared state among untrusted parties. In 2026, P2P is production infrastructure: WebRTC powers video calls, IPFS hosts censorship-resistant content, and gossip protocols coordinate distributed databases and container orchestration systems. The architecture is not primarily about ideology — it solves real engineering problems: bandwidth cost distribution, resilience without a single point of failure, and coordination among parties who do not trust a central authority.
Read next: Pipe and Filter: Data Processing Pipelines →
Part of the Software Architecture Hub — engineering the descent.
