---
# System prepended metadata

title: 'Building My Own P2P Chat App: A Journey into Distributed Systems'

---

# Building My Own P2P Chat App: A Journey into Distributed Systems

> You know that feeling when WhatsApp goes down and you can't text anyone? Or when you realize all your messages go through someone else's computer before reaching your friend? Yeah, that bugged me too.

> So I thought: "What if I could send messages directly to my friends, without any company in the middle?" Turns out, that's exactly what peer-to-peer (P2P) means. And building one? Best learning experience ever.

> Let me tell you about my journey building a P2P chat app in Rust, and trust me, if I can do it, you definitely can too.

## What Even Is P2P? 

Imagine you and your friends are passing notes in class.

**Normal apps (like WhatsApp):**

You write a note → give it to the teacher → teacher gives it to your friend and The teacher reads all your notes and decides who gets what. If the teacher's busy (**server down**), nobody gets any notes.

**P2P apps:**

You write a note → hand it directly to your friend

No middleman. No one reading your stuff. Just you and your friend talking directly.

But here's the cool part: what if you have 5 friends? You tell one friend, they tell two others, and boom everyone knows. That's called "gossip protocol" (yes, really!).

## Why Did I Build This?

### Reason #1: I Was Curious
I kept hearing about Bitcoin, IPFS, and all these "decentralized" things. But reading about them? Boring. Building one? Now we're talking!

### Reason #3: Actually Useful
I wanted something that actually works, not just a toy. This app can legitimately replace your group chat, no servers, no monthly fees, no one snooping.

### Reason #4: It Looked Impossible (Challenge Accepted!)
Protocol engineering? Distributed systems? Cryptography? These words sounded scary. But you know what? Breaking them down into small pieces makes them totally doable.

## What I Built

Okay, so here's what the app can do:

**The Basics:**
- You run it on your computer
- Your friend runs it on theirs
- You type messages, they see them instantly
- No server in the middle

**The Cool Stuff:**
- Works with unlimited people (I tested it with 20+ peers)
- Messages spread automatically through "gossip" (more on this later)
- Cryptographic signatures so no one can fake messages from you
- Saves chat history to disk
- Looks pretty in the terminal with colors

**The Technical Magic:**
- Built in Rust (blazing fast!)
- Uses fancy algorithms like epidemic broadcast
- Ed25519 cryptography (the same stuff cryptocurrencies use)
- Async programming so it can handle thousands of connections

## The Building Journey (The Real Talk)

### Week 1: "This Is Easy!"

Started with connecting two computers. Just TCP sockets, right? How hard can it be?

*Narrator: It was harder than he thought.*

**First challenge:** How do you know where one message ends and another begins? On the network, it's just a stream of bytes. Could be one long message or ten short ones all squished together.

**Solution:** Put the message length at the start. Like writing "This note has 12 letters" before your actual note. Simple but genius.

```rust
// Send message: [4 bytes for length][actual message]
let length = message.len() as u32;
write_to_socket(&length.to_be_bytes());  // Tell them how long it is
write_to_socket(&message);                // Send the actual message
```

### Week 2: "Wait, Async What Now?"

Okay so now I need to both read messages AND write messages at the same time. Can't just sit there waiting for incoming messages, gotta send my own too!

Enter: `tokio::select!`

Think of it like this: You're cooking dinner (reading messages) but also need to answer the door when the pizza arrives (sending messages). You can't do both at once, but you can quickly switch between checking the stove and listening for the doorbell.

```rust
loop {
    tokio::select! {
        // Kitchen timer goes off? Handle cooking
        msg = read_from_socket() => { handle_incoming(msg); }
        
        // Doorbell rings? Get the pizza
        msg = get_user_input() => { send_message(msg); }
    }
}
```

This blew my mind. One thread doing two things!

### Week 3: The Gossip Protocol (My Favorite Part)

Here's where it gets really cool. How do you send a message to 100 people without actually sending it 100 times?

**Gossip!**

Remember playground rumors? One kid tells three kids, those three each tell three more, and by lunchtime the whole school knows.

Same idea:
1. I send a message to 3 random friends
2. Each of them sends it to 3 of *their* friends
3. Repeat until everyone has it

Math time: 3 → 9 → 27 → 81 → 243... everyone knows in just 5 "hops"!

But wait, problems:
- **Problem 1:** What if Blessing tells Bob, and Bob tells Blessing again?
  **Solution:** Remember message IDs we've seen. "Already heard that one, thanks."

- **Problem 2:** What if it goes in circles forever?
  **Solution:** Give each message a "Time To Live" (TTL). Decrease it each hop. When it hits zero, stop forwarding.
  
```rust
// Check if we've seen this message before
if seen_messages.contains(message.id) {
    return; // Yep, already got it
}

// Check if it's expired
if message.ttl == 0 {
    return; // This message is too old
}

// Forward to 3 random friends
let friends = pick_random_friends(3);
for friend in friends {
    send_to(friend, message.with_ttl_decreased());
}
```

Beautiful, right?

### Week 4: "Let's Add Crypto!" (The Good Kind)

So anyone can send messages claiming to be me, huh? That's not good.

Time for signatures! Like signing a paper contract, but **digital**.

**How it works (simple version):**
1. I have a secret key (private) that only I know
2. I have a public key that everyone can see
3. I "sign" my messages with my secret key
4. Anyone can verify it's really from me using my public key

It's like a seal on a royal letter. You can see the seal (public), verify it's real, but you can't make one yourself without the special stamp (private key).

```rust
// When sending:
let my_secret = load_my_secret_key();
let signature = sign(message, my_secret);
send(message, signature);

// When receiving:
let their_public_key = get_sender_public_key();
if verify(message, signature, their_public_key) {
    println!("Legit message from {}", sender);
} else {
    println!("Fake! Someone's trying to impersonate!");
}
```

I used [Ed25519](https://crates.io/crates/ed25519-dalek) – same crypto Bitcoin uses. If it's good enough for billions of dollars, it's good enough for my memes.

## What I Actually Learned (The Gold)

### 1. Async Programming Isn't Scary

Once you understand it's just "do many things without blocking," it clicks. [Tokio](https://crates.io/crates/tokio) makes this almost easy.

**Before:** "Async/await is confusing!"
**After:** "Oh, it's just tasks that can pause and let others run. Cool!"

### 2. Distributed Systems Are Everywhere

Netflix, Uber, your multiplayer games, they all use these patterns. Gossip protocols, eventual consistency, peer discovery... they're not academic concepts, they're real tools solving real problems.

### 3. Rust Is Actually Amazing

Yeah, the borrow checker yells at you. But you know what? It's usually right. Once your code compiles, it just... works. No random crashes at 3 AM.

Plus it's FAST. Like, C++ fast. My app handles 10,000 messages per second easy.

### 4. Cryptography Isn't Magic

It's just math. Complicated math, sure, but there are libraries that make it simple. Understanding *why* it works is more important than implementing it from scratch.

### 5. Documentation Is Your Best Friend

I spent SO much time reading docs. Tokio docs, Rust book, protocol specs. Good documentation is worth its weight in gold.

### 6. Start Simple, Add Complexity Later

My first version? Two peers, basic text. That's it.

Then I added:
- More peers
- Gossip algorithm  
- Signatures
- Persistence
- Pretty colors

If I tried to build everything at once? I'd have quit in frustration.

### 7. Testing Saves Your Sanity

Write tests. Seriously. When I changed the gossip algorithm, my tests caught THREE bugs before I even ran the app.

```rust
#[test]
fn test_message_not_forwarded_twice() {
    let mut gossip = create_test_gossip();
    let msg = create_test_message();
    
    // First time: should forward
    assert!(gossip.should_forward(&msg));
    
    // Second time: should NOT forward (duplicate)
    assert!(!gossip.should_forward(&msg));
}
```

## The Tricky Bits (And How I Solved Them)

### Challenge 1: Messages Arriving Out of Order

On the network, messages don't arrive in a neat line. Message 3 might arrive before message 1.

**Solution:** Add timestamps and UUIDs. Let the app figure out the order.

### Challenge 2: Peers Disconnecting Randomly

People close their laptops, internet drops, whatever. Connections die.

**Solution:** Detect dead connections (using ping/pong), clean up gracefully, try to reconnect with backoff.

### Challenge 3: Network at 100% CPU

Early version forwarded messages to EVERYONE. With 50 peers, that's 50 sends per message. Oops.

**Solution:** Gossip protocol! Only forward to 3-5 random peers. Problem solved, CPU happy.

### Challenge 4: The Borrow Checker

Rust's borrow checker is... strict. You can't have two parts of your code modifying the same data.

**Solution:** Use channels! One part owns the data, others send it messages. Like having a manager handle requests instead of everyone grabbing stuff.

```rust
// Instead of sharing mutable state:
let peers = Arc<Mutex>(); // Complicated!

// Use channels:
let (sender, receiver) = channel();
// One task owns the data, others send messages
```

## Cool Things I Discovered

**Discovery 1:** Most of networking is just "send some bytes, receive some bytes." The fancy stuff is *what* you send and *when*.

**Discovery 2:** Rust's error handling is brilliant. `Result<T, Error>` forces you to think about failures. No more "oops forgot error handling."

**Discovery 3:** The gossip algorithm is used EVERYWHERE. Cassandra (database), Bitcoin (transaction spreading), Consul (service discovery)... it's a fundamental pattern.

**Discovery 4:** You don't need a CS degree. Just curiosity and willingness to read docs and experiment.

**Discovery 5:** Building something real is 10x better than tutorials. You hit real problems with real solutions.

## If I Started Over, Here's What I'd Do Different

1. **Write tests first** – Would've saved me hours debugging
2. **Design the protocol on paper** – I changed my message format 3 times. Paper first, code second.
3. **Use existing libraries more** – I tried to reinvent wheels. Don't. Use libraries for crypto, serialization, etc.
4. **Add logging earlier** – When bugs happen (and they will), logs are gold.
5. **Start with fewer features** – Get ONE thing working really well, then add more.

## Why You Should Build Something Like This

Look, watching tutorials is fine. Reading books is great. But building something *real*? That's where the magic happens.

Here's what you get:
- **Real understanding** – Not just "I read about TCP" but "I debugged TCP issues for 3 hours"
- **Portfolio material** – Way better than "I did a todo app"
- **Confidence** – If you can build this, you can build anything
- **Actual useful tool** – I literally use this to chat with friends
- **Interview stories** – "Tell me about a challenging project" – boom, got one

Plus it's just... fun? There's something magical about typing a message on your laptop and seeing it appear on your friend's computer across the world, knowing YOU built the entire thing.

## The Metrics (Numbers Are Cool)

Just so you know this isn't a toy:

- **2,062 lines** of Rust code
- **~10,000 messages/second** throughput
- **<1ms latency** on local network
- **Tested with 500 peers** simultaneously
- **~5KB memory** per peer connection
- **0 servers** needed (the whole point!)

## My Favorite Moment

Testing with 5 friends, all of us connected to each other's computers. I sent "Hello world" and watched it propagate through the network in real-time with debug logging on.

```
[Me] Sending to: Dice, Bob
[Dice] Received! Forwarding to: Bayo
[Bob] Received! Forwarding to: Taiwo, Bamijo
[Bayo] Already seen this
[Taiwo] Received! Forwarding to: Bamijo
[Bamijo] Received! Already got it from Bob
```
Seeing the gossip algorithm actually work in real life? Chef's kiss. 🤌

## Final Words

Building this P2P chat taught me more than any course ever could. It's messy, frustrating, and absolutely worth it.

You'll Google a lot or use AI tools like ChatGPT to ask questions and learn from it. You'll read documentation until your eyes hurt. You'll encounter bugs that make zero sense. And then suddenly it works and you feel like a wizard.

**Here's my advice:**

**Start small.** Don't try to build WhatsApp on day one. Get two computers talking. That's huge!

**Break it down.** Every big problem is just small problems stacked in a trench coat. Tackle them one at a time.

**Don't fear Rust.** Yeah, the borrow checker is strict. But it's teaching you to write better code. Trust the process.

**Read the docs.** Seriously. The Rust book is free and amazing. Tokio docs are great. Use them.

**Join communities.** r/rust, Discord servers, forums. People are helpful and you'll learn tons.

**Have fun!** If it stops being fun, take a break. Come back fresh. This isn't a race.

## What's Next For Me

Now that I have a working P2P chat, I'm thinking:
- Maybe add voice calls?
- File sharing could be cool
- Add Frontend for it and host it on a server
- A web interface might make it more accessible
- Or maybe I'll build something completely different

The point is: now I *know* I can build this stuff. And that confidence? Priceless.

## Want To Build Your Own?

Here's the thing: you don't need to be a genius. You don't need 10 years of experience. You need:
1. Curiosity
2. Patience  
3. Willingness to Google stuff
4. Time (be realistic, this takes weeks not days)

Start with something simpler if you want:
- Build a basic TCP chat (no P2P, just client-server)
- Then make it async
- Then add multiple clients
- Then remove the server (boom, P2P!)
- Then add gossip
- Then add crypto

Or go wild and build it all at once. Both work!

## Resources That Saved My Life

- [**The Rust Book**](https://doc.rust-lang.org/book/) – Start here. It's free and excellent.
- [**Tokio Tutorial**](https://www.youtube.com/watch?v=dOzrO40jgbU) – For async/await stuff
- [**Jon Gjengset's YouTube**](https://www.youtube.com/c/JonGjengset) – Rust content by a wizard
- [**"Designing Data-Intensive Applications"**](https://unidel.edu.ng/focelibrary/books/Designing%20Data-Intensive%20Applications%20The%20Big%20Ideas%20Behind%20Reliable,%20Scalable,%20and%20Maintainable%20Systems%20by%20Martin%20Kleppmann%20(z-lib.org).pdf) by Kleppmann – Not Rust-specific but explains distributed systems beautifully
- [**Claude AI**](https://www.claude.ai/) – Obviously

## One Last Thing

8 months ago, I didn't know what P2P meant. Four months ago, I'd never written Rust.

Now I have a working P2P chat app with crypto, gossip protocols, and persistence.

If I can do it, you absolutely can too.

So what are you waiting for? Go build something cool! 🚀

---

*P.S. – If you build something inspired by this, I'd love to hear about it! Tag me or drop a comment. Let's learn together.*

*P.P.S. – Yes, the code is available. Yes, check my this repo for it ([github](https://github.com/dicethedev/p2p-chat)) it actually works. Yes, you can use it. Have fun!* 