basic chat app
This commit is contained in:
@@ -1,17 +1,25 @@
|
||||
# Phase 7: Building Your First P2P App
|
||||
|
||||
## Let's Build Something!
|
||||
## Let's Build Something Real!
|
||||
|
||||
Now that you understand the concepts, let's build an actual P2P application together.
|
||||
|
||||
We'll build a **P2P Chat App** - step by step.
|
||||
We'll build a **P2P Chat App** - step by step. By the end, you'll have a working chat app where multiple people can talk directly to each other without any server!
|
||||
|
||||
---
|
||||
|
||||
## What We're Building
|
||||
|
||||
A simple anonymous chat app where:
|
||||
- Everyone joins the same chat room automatically
|
||||
- Messages go directly from one peer to another
|
||||
- No server needed - just run the code
|
||||
- Works between any two computers on the internet
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before we start, make sure you have:
|
||||
|
||||
### 1. Node.js Installed
|
||||
|
||||
```bash
|
||||
@@ -24,18 +32,18 @@ node --version
|
||||
### 2. Create a Project Folder
|
||||
|
||||
```bash
|
||||
mkdir my-p2p-chat
|
||||
cd my-p2p-chat
|
||||
mkdir hyperswarm-chat
|
||||
cd hyperswarm-chat
|
||||
npm init -y
|
||||
```
|
||||
|
||||
### 3. Install Dependencies
|
||||
### 3. Install Hyperswarm
|
||||
|
||||
```bash
|
||||
npm install hypercore hyperswarm
|
||||
npm install hyperswarm
|
||||
```
|
||||
|
||||
That's it! Just two packages to build a P2P chat app.
|
||||
That's it! One package - that's all you need for P2P networking.
|
||||
|
||||
---
|
||||
|
||||
@@ -44,11 +52,55 @@ That's it! Just two packages to build a P2P chat app.
|
||||
Create a file called `chat.js`:
|
||||
|
||||
```javascript
|
||||
// Step 1: Import our tools
|
||||
const Hypercore = require('hypercore')
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
const crypto = require('crypto')
|
||||
|
||||
console.log('🚀 Starting P2P Chat...')
|
||||
console.log('Starting P2P Chat...')
|
||||
|
||||
// Create a topic (this determines our chat room)
|
||||
const ROOM = 'basic-p2p-chat-2026'
|
||||
const topic = crypto.createHash('sha256').update(ROOM).digest()
|
||||
|
||||
console.log('Topic:', topic.toString('hex'))
|
||||
```
|
||||
|
||||
Run it:
|
||||
|
||||
```bash
|
||||
node chat.js
|
||||
```
|
||||
|
||||
You should see a hex string - that's your topic key.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Create the Swarm
|
||||
|
||||
Now let's create the P2P networking layer:
|
||||
|
||||
```javascript
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
const crypto = require('crypto')
|
||||
|
||||
async function start() {
|
||||
const ROOM = 'basic-p2p-chat-2026'
|
||||
const topic = crypto.createHash('sha256').update(ROOM).digest()
|
||||
|
||||
// Create the swarm
|
||||
const swarm = new Hyperswarm()
|
||||
|
||||
console.log('Swarm created, joining room...')
|
||||
|
||||
// Join the chat room - both as server and client
|
||||
const discovery = swarm.join(topic, { server: true, client: true })
|
||||
|
||||
// Wait for discovery to flush (find peers)
|
||||
await discovery.flushed()
|
||||
|
||||
console.log('Connected to chat room!')
|
||||
}
|
||||
|
||||
start()
|
||||
```
|
||||
|
||||
Run it:
|
||||
@@ -56,462 +108,360 @@ Run it:
|
||||
node chat.js
|
||||
```
|
||||
|
||||
You should see: `🚀 Starting P2P Chat...`
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Create a Feed
|
||||
## Step 3: Handle Connections
|
||||
|
||||
Add the Hypercore setup:
|
||||
Now let's handle when peers connect:
|
||||
|
||||
```javascript
|
||||
const Hypercore = require('hypercore')
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
const crypto = require('crypto')
|
||||
|
||||
console.log('🚀 Starting P2P Chat...')
|
||||
async function start() {
|
||||
const ROOM = 'basic-p2p-chat-2026'
|
||||
const topic = crypto.createHash('sha256').update(ROOM).digest()
|
||||
|
||||
// Create a feed (your personal message log)
|
||||
// This is like your personal notebook
|
||||
const core = new Hypercore('./chat-storage')
|
||||
const swarm = new Hyperswarm()
|
||||
const connections = new Set()
|
||||
|
||||
// Wait for it to be ready
|
||||
core.ready().then(() => {
|
||||
console.log('📓 Feed created!')
|
||||
console.log(' Your public key:', core.key.toString('hex').slice(0, 16) + '...')
|
||||
console.log(' Messages so far:', core.length)
|
||||
})
|
||||
```
|
||||
// Handle incoming connections
|
||||
swarm.on('connection', (conn) => {
|
||||
connections.add(conn)
|
||||
console.log('[+] New peer connected')
|
||||
|
||||
Run it:
|
||||
```bash
|
||||
node chat.js
|
||||
```
|
||||
// Handle incoming messages
|
||||
conn.on('data', (data) => {
|
||||
console.log('Received:', data.toString())
|
||||
})
|
||||
|
||||
You should see something like:
|
||||
```
|
||||
🚀 Starting P2P Chat...
|
||||
📓 Feed created!
|
||||
Your public key: a1b2c3d4e5f6...
|
||||
Messages so far: 0
|
||||
// Handle disconnection
|
||||
conn.on('close', () => {
|
||||
connections.delete(conn)
|
||||
console.log('[-] Peer disconnected')
|
||||
})
|
||||
})
|
||||
|
||||
const discovery = swarm.join(topic, { server: true, client: true })
|
||||
await discovery.flushed()
|
||||
|
||||
console.log('Ready to chat!')
|
||||
}
|
||||
|
||||
start()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Add Networking
|
||||
## Step 4: Add Chat Interface
|
||||
|
||||
Now let's make it discoverable:
|
||||
Now let's add the ability to type and send messages:
|
||||
|
||||
```javascript
|
||||
const Hypercore = require('hypercore')
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
|
||||
console.log('🚀 Starting P2P Chat...')
|
||||
|
||||
// Create feed
|
||||
const core = new Hypercore('./chat-storage')
|
||||
|
||||
// Create swarm (networking)
|
||||
const swarm = new Hyperswarm()
|
||||
|
||||
core.ready().then(() => {
|
||||
console.log('📓 Feed ready!')
|
||||
console.log(' Key:', core.key.toString('hex').slice(0, 16) + '...')
|
||||
|
||||
// Join the swarm using our feed's discovery key
|
||||
// This makes us findable by others interested in this chat
|
||||
swarm.join(core.discoveryKey, { server: true, client: true })
|
||||
|
||||
console.log('🌐 Looking for peers...')
|
||||
})
|
||||
|
||||
// When we find a peer
|
||||
swarm.on('connection', (connection, info) => {
|
||||
console.log('✅ Peer connected!', info.peer.publicKey.toString('hex').slice(0, 8))
|
||||
|
||||
// Replicate with them (share messages)
|
||||
core.replicate(connection)
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Send Messages
|
||||
|
||||
Now let's add the ability to send messages:
|
||||
|
||||
```javascript
|
||||
const Hypercore = require('hypercore')
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
const crypto = require('crypto')
|
||||
const readline = require('readline')
|
||||
|
||||
console.log('🚀 Starting P2P Chat...')
|
||||
async function start() {
|
||||
const ROOM = 'basic-p2p-chat-2026'
|
||||
const topic = crypto.createHash('sha256').update(ROOM).digest()
|
||||
|
||||
const core = new Hypercore('./chat-storage')
|
||||
const swarm = new Hyperswarm()
|
||||
const swarm = new Hyperswarm()
|
||||
const connections = new Set()
|
||||
|
||||
// Create interface for typing
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
})
|
||||
swarm.on('connection', (conn) => {
|
||||
connections.add(conn)
|
||||
console.log('\n[+] New peer connected')
|
||||
|
||||
core.ready().then(() => {
|
||||
console.log('📓 Feed ready!')
|
||||
console.log(' Share this key with friends:', core.key.toString('hex'))
|
||||
console.log()
|
||||
|
||||
swarm.join(core.discoveryKey, { server: true, client: true })
|
||||
|
||||
// Listen for new messages
|
||||
core.on('append', () => {
|
||||
// New message arrived! Show it.
|
||||
showLatestMessages()
|
||||
})
|
||||
|
||||
// Start chat
|
||||
promptForMessage()
|
||||
})
|
||||
|
||||
function promptForMessage() {
|
||||
rl.question('You: ', (message) => {
|
||||
if (message.trim()) {
|
||||
// Send message to feed
|
||||
core.append(Buffer.from(message))
|
||||
}
|
||||
promptForMessage() // Keep asking
|
||||
})
|
||||
}
|
||||
|
||||
function showLatestMessages() {
|
||||
// Show last 5 messages
|
||||
const start = Math.max(0, core.length - 5)
|
||||
for (let i = start; i < core.length; i++) {
|
||||
core.get(i).then(data => {
|
||||
console.log('📨 Friend:', data.toString())
|
||||
conn.on('data', (data) => {
|
||||
console.log(`\nFriend: ${data.toString().trim()}`)
|
||||
rl.prompt()
|
||||
})
|
||||
}
|
||||
|
||||
conn.on('close', () => {
|
||||
connections.delete(conn)
|
||||
console.log('\n[-] Peer disconnected')
|
||||
})
|
||||
})
|
||||
|
||||
const discovery = swarm.join(topic, { server: true, client: true })
|
||||
await discovery.flushed()
|
||||
|
||||
console.log('\n=== P2P Chat Ready ===')
|
||||
console.log('Type a message and press Enter!\n')
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
prompt: '> '
|
||||
})
|
||||
rl.prompt()
|
||||
|
||||
rl.on('line', (input) => {
|
||||
const msg = input.trim()
|
||||
if (!msg) {
|
||||
rl.prompt()
|
||||
return
|
||||
}
|
||||
|
||||
// Show our own message
|
||||
console.log(`\nYou: ${msg}`)
|
||||
|
||||
// Send to all connected peers
|
||||
for (const conn of connections) {
|
||||
if (!conn.destroyed) {
|
||||
conn.write(msg)
|
||||
}
|
||||
}
|
||||
|
||||
rl.prompt()
|
||||
})
|
||||
}
|
||||
|
||||
start()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Full Working App
|
||||
## Step 5: The Complete Chat App
|
||||
|
||||
Here's the complete chat app:
|
||||
Here's the complete, working P2P chat application:
|
||||
|
||||
```javascript
|
||||
const Hypercore = require('hypercore')
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
const crypto = require('crypto')
|
||||
const readline = require('readline')
|
||||
|
||||
console.log('╔═══════════════════════════════════╗')
|
||||
console.log('║ 🚀 P2P Chat App v1.0 ║')
|
||||
console.log('╚═══════════════════════════════════╝')
|
||||
|
||||
const core = new Hypercore('./my-chat')
|
||||
const swarm = new Hyperswarm()
|
||||
async function start() {
|
||||
// Fixed room — change this string only if you want a different room
|
||||
const ROOM = 'basic-p2p-chat-2026'
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
})
|
||||
|
||||
core.ready().then(() => {
|
||||
console.log('\n📓 Your chat feed is ready!')
|
||||
console.log(' Share this key with friends to chat:')
|
||||
console.log(' ' + core.key.toString('hex').slice(0, 32) + '...\n')
|
||||
|
||||
// Join swarm
|
||||
swarm.join(core.discoveryKey, { server: true, client: true })
|
||||
|
||||
// Handle connections
|
||||
swarm.on('connection', (conn, info) => {
|
||||
console.log('✅ Peer connected!')
|
||||
core.replicate(conn)
|
||||
})
|
||||
|
||||
// Show messages when they arrive
|
||||
core.on('append', showMessages)
|
||||
|
||||
// Start the chat
|
||||
console.log('💬 Type a message and press Enter!\n')
|
||||
prompt()
|
||||
})
|
||||
const topic = crypto.createHash('sha256').update(ROOM).digest()
|
||||
|
||||
function prompt() {
|
||||
rl.question('You: ', (msg) => {
|
||||
if (msg.trim()) {
|
||||
core.append(Buffer.from(msg))
|
||||
}
|
||||
prompt()
|
||||
})
|
||||
}
|
||||
|
||||
function showMessages() {
|
||||
console.log('\n--- Chat History ---')
|
||||
for (let i = 0; i < core.length; i++) {
|
||||
core.get(i).then(msg => {
|
||||
console.log(' ' + msg.toString())
|
||||
const swarm = new Hyperswarm()
|
||||
const connections = new Set()
|
||||
|
||||
|
||||
swarm.on('connection', (conn) => {
|
||||
connections.add(conn)
|
||||
console.log('\n[+] New peer connected')
|
||||
|
||||
|
||||
conn.on('data', (data) => {
|
||||
process.stdout.write(`\n${data.toString().trim()}\n> `)
|
||||
})
|
||||
|
||||
|
||||
conn.on('close', () => {
|
||||
connections.delete(conn)
|
||||
console.log('\n[-] Peer disconnected')
|
||||
})
|
||||
|
||||
|
||||
conn.on('error', () => {}) // ignore common network errors
|
||||
})
|
||||
|
||||
|
||||
// Join the room (both server + client)
|
||||
const discovery = swarm.join(topic, { server: true, client: true })
|
||||
await discovery.flushed()
|
||||
|
||||
|
||||
console.log(`\n=== Anonymous Hyperswarm Chat ===`)
|
||||
console.log(`Room: ${ROOM}`)
|
||||
console.log(`Just type and press Enter to chat.\nCommands: /peers /quit\n`)
|
||||
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
prompt: '> '
|
||||
})
|
||||
rl.prompt()
|
||||
|
||||
|
||||
rl.on('line', (input) => {
|
||||
const msg = input.trim()
|
||||
if (!msg) {
|
||||
rl.prompt()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (msg === '/quit' || msg === '/exit') {
|
||||
cleanup()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (msg === '/peers') {
|
||||
console.log(`Connected peers: ${connections.size}`)
|
||||
rl.prompt()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Local echo + broadcast to everyone
|
||||
process.stdout.write(`\n${msg}\n> `)
|
||||
|
||||
|
||||
for (const conn of connections) {
|
||||
if (!conn.destroyed) {
|
||||
conn.write(msg + '\n')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rl.prompt()
|
||||
})
|
||||
|
||||
|
||||
async function cleanup() {
|
||||
rl.close()
|
||||
for (const conn of connections) conn.destroy()
|
||||
await swarm.destroy()
|
||||
console.log('\nDisconnected. Goodbye!')
|
||||
process.exit(0)
|
||||
}
|
||||
console.log('-------------------\n')
|
||||
|
||||
|
||||
process.on('SIGINT', cleanup)
|
||||
}
|
||||
|
||||
|
||||
start().catch(err => {
|
||||
console.error('Error:', err.message)
|
||||
process.exit(1)
|
||||
})
|
||||
```
|
||||
|
||||
Save this as `chat.js` and run:
|
||||
|
||||
```bash
|
||||
node chat.js
|
||||
```
|
||||
Save this as `chat.js` and you're done!
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Testing It Works
|
||||
|
||||
To test with yourself:
|
||||
|
||||
1. Open two terminal windows
|
||||
2. Run `node chat.js` in both
|
||||
3. Copy the key from one terminal to the other
|
||||
4. Type messages!
|
||||
|
||||
Actually, let me fix that - there's a bug. Let me make it cleaner:
|
||||
|
||||
```javascript
|
||||
// Simpler version - just run in two terminals
|
||||
// They will find each other automatically!
|
||||
|
||||
const Hypercore = require('hypercore')
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
const readline = require('readline')
|
||||
|
||||
const core = new Hypercore('./chat-' + Date.now())
|
||||
const swarm = new Hyperswarm()
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
})
|
||||
|
||||
core.ready().then(() => {
|
||||
console.log('\n📓 Feed ready!')
|
||||
console.log(' Key:', core.key.toString('hex'))
|
||||
console.log(' (Run this in another terminal and paste the key!)\n')
|
||||
|
||||
swarm.join(core.discoveryKey, { server: true, client: true })
|
||||
|
||||
swarm.on('connection', (conn, info) => {
|
||||
console.log('\n✅ PEER CONNECTED!\n')
|
||||
core.replicate(conn)
|
||||
})
|
||||
|
||||
// Listen for messages
|
||||
core.on('append', () => {
|
||||
const len = core.length
|
||||
core.get(len - 1).then(msg => {
|
||||
console.log('\n📨 Received:', msg.toString())
|
||||
})
|
||||
})
|
||||
|
||||
prompt()
|
||||
})
|
||||
|
||||
function prompt() {
|
||||
rl.question('You: ', (msg) => {
|
||||
if (msg.trim()) {
|
||||
core.append(Buffer.from(msg))
|
||||
}
|
||||
prompt()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### To Test:
|
||||
|
||||
**Terminal 1:**
|
||||
### Terminal 1 - Start the chat:
|
||||
```bash
|
||||
node chat.js
|
||||
# Copy the key it shows
|
||||
```
|
||||
|
||||
You'll see:
|
||||
```
|
||||
=== Anonymous Hyperswarm Chat ===
|
||||
Room: basic-p2p-chat-2026
|
||||
Just type and press Enter to chat.
|
||||
Commands: /peers /quit
|
||||
```
|
||||
|
||||
### Terminal 2 - Join the same chat:
|
||||
```bash
|
||||
node chat.js
|
||||
```
|
||||
|
||||
### Watch for connection!
|
||||
|
||||
After a few seconds (first time can take 5-60 seconds for DHT discovery and NAT hole-punching), you'll see:
|
||||
|
||||
**Terminal 1:**
|
||||
```
|
||||
[+] New peer connected
|
||||
```
|
||||
|
||||
**Terminal 2:**
|
||||
```bash
|
||||
node chat.js
|
||||
# It will find Terminal 1 automatically!
|
||||
# (They share the same discovery key)
|
||||
```
|
||||
[+] New peer connected
|
||||
```
|
||||
|
||||
Now type in either terminal and press Enter - the message will appear in both!
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Adding File Sharing
|
||||
## How It Works
|
||||
|
||||
Now let's add file sharing to our chat!
|
||||
### The Room Concept
|
||||
|
||||
```
|
||||
const ROOM = 'basic-p2p-chat-2026'
|
||||
const topic = crypto.createHash('sha256').update(ROOM).digest()
|
||||
```
|
||||
|
||||
- We pick a room name (any string)
|
||||
- We hash it to get a 32-byte topic
|
||||
- Everyone who uses the same room name gets the same topic
|
||||
- The DHT uses this topic to help peers find each other
|
||||
|
||||
### Joining the Swarm
|
||||
|
||||
```javascript
|
||||
const Hypercore = require('hypercore')
|
||||
const Hyperdrive = require('hyperdrive')
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
const fs = require('fs')
|
||||
const readline = require('readline')
|
||||
|
||||
console.log('📎 P2P Chat + File Sharing!')
|
||||
|
||||
const core = new Hypercore('./chat-storage')
|
||||
const drive = new Hyperdrive(core)
|
||||
const swarm = new Hyperswarm()
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
})
|
||||
|
||||
core.ready().then(() => {
|
||||
drive.ready().then(() => {
|
||||
console.log('📓 Ready! Key:', core.key.toString('hex').slice(0, 16) + '...')
|
||||
|
||||
swarm.join(drive.discoveryKey, { server: true, client: true })
|
||||
swarm.join(core.discoveryKey, { server: true, client: true })
|
||||
|
||||
swarm.on('connection', (conn) => {
|
||||
console.log('🔗 Peer connected!')
|
||||
core.replicate(conn)
|
||||
drive.replicate(conn)
|
||||
})
|
||||
|
||||
// Handle commands
|
||||
console.log('\nCommands:')
|
||||
console.log(' msg <text> - Send a message')
|
||||
console.log(' file <filepath> - Send a file\n')
|
||||
|
||||
prompt()
|
||||
})
|
||||
})
|
||||
|
||||
function prompt() {
|
||||
rl.question('> ', async (input) => {
|
||||
const parts = input.split(' ')
|
||||
const cmd = parts[0]
|
||||
const arg = parts.slice(1).join(' ')
|
||||
|
||||
if (cmd === 'msg' && arg) {
|
||||
await core.append(Buffer.from(arg))
|
||||
console.log('✅ Message sent!')
|
||||
} else if (cmd === 'file' && arg) {
|
||||
try {
|
||||
const content = fs.readFileSync(arg)
|
||||
await drive.put('/' + arg, content)
|
||||
console.log('✅ File sent! Key: /' + arg)
|
||||
} catch (e) {
|
||||
console.log('❌ Error:', e.message)
|
||||
}
|
||||
} else if (cmd === 'list') {
|
||||
for await (const file of drive.readdir('/')) {
|
||||
console.log(' 📄', file)
|
||||
}
|
||||
} else if (cmd === 'get' && arg) {
|
||||
const content = await drive.get('/' + arg)
|
||||
console.log('📄 File content:', content ? content.toString() : 'not found')
|
||||
}
|
||||
|
||||
prompt()
|
||||
})
|
||||
}
|
||||
const discovery = swarm.join(topic, { server: true, client: true })
|
||||
await discovery.flushed()
|
||||
```
|
||||
|
||||
---
|
||||
- `server: true` - Accept connections from other peers
|
||||
- `client: true` - Connect to other peers we discover
|
||||
- `discovery.flushed()` - Wait until we're fully connected to the network
|
||||
|
||||
## Step 8: Offline-First
|
||||
|
||||
One of P2P's superpowers: **offline-first**!
|
||||
### Sending Messages
|
||||
|
||||
```javascript
|
||||
// This app works OFFLINE!
|
||||
// Here's how:
|
||||
|
||||
const core = new Hypercore('./offline-chat')
|
||||
const swarm = new Hyperswarm()
|
||||
|
||||
// 1. APPEND WORKS OFFLINE
|
||||
// You can always add to your feed
|
||||
await core.append(Buffer.from('Message'))
|
||||
|
||||
// Even without internet!
|
||||
// It just waits in your local storage
|
||||
|
||||
// 2. WHEN ONLINE, IT SYNCS
|
||||
core.ready().then(() => {
|
||||
swarm.join(core.discoveryKey)
|
||||
|
||||
swarm.on('connection', conn => {
|
||||
// Automatically syncs when peer found
|
||||
core.replicate(conn)
|
||||
console.log('🔄 Synced!')
|
||||
})
|
||||
})
|
||||
|
||||
// 3. YOU DON'T NEED TO CHANGE YOUR CODE!
|
||||
// Just append, and it syncs when possible
|
||||
conn.write(msg)
|
||||
```
|
||||
|
||||
- Each connection is a simple stream
|
||||
- We just write raw data to it
|
||||
- The receiving end gets it via the 'data' event
|
||||
|
||||
---
|
||||
|
||||
## Step 9: Making It Distributable
|
||||
## Customizing the Chat Room
|
||||
|
||||
Want to share your app? Use Pear!
|
||||
Want a different chat room? Just change the `ROOM` variable:
|
||||
|
||||
```bash
|
||||
# Install Pear
|
||||
npm install -g pear
|
||||
|
||||
# Initialize Pear project
|
||||
pear init my-chat-app
|
||||
|
||||
# Put your code in app.js
|
||||
|
||||
# Run locally
|
||||
pear run
|
||||
|
||||
# Build for distribution
|
||||
pear build
|
||||
```javascript
|
||||
const ROOM = 'my-secret-room-123'
|
||||
```
|
||||
|
||||
Now others can install your app directly from you!
|
||||
Everyone who uses the same room name will find each other!
|
||||
|
||||
---
|
||||
|
||||
## What We've Built
|
||||
## Features Included
|
||||
|
||||
| Feature | Code |
|
||||
|---------|------|
|
||||
| ✅ P2P Chat | Hypercore + Hyperswarm |
|
||||
| ✅ Peer Discovery | Auto via DHT |
|
||||
| ✅ Message Sync | core.replicate() |
|
||||
| ✅ File Sharing | Hyperdrive |
|
||||
| ✅ Offline-First | Built-in! |
|
||||
| ✅ Distribution | Pear ready! |
|
||||
| Feature | How It's Done |
|
||||
|---------|---------------|
|
||||
| **P2P Networking** | Hyperswarm |
|
||||
| **Peer Discovery** | HyperDHT (built into Hyperswarm) |
|
||||
| **Direct Connections** | NAT hole-punching |
|
||||
| **Room System** | SHA-256 hash of room name |
|
||||
| **Message Broadcasting** | Loop through all connections |
|
||||
| **Commands** | /peers, /quit |
|
||||
|
||||
---
|
||||
|
||||
## Challenges to Try
|
||||
|
||||
1. **Add usernames** - Store a username with each message
|
||||
2. **Add timestamps** - When was each message sent?
|
||||
3. **Make it multi-room** - Different topics for different chats
|
||||
4. **Add encryption** - Only decrypt messages for friends
|
||||
5. **Add file download** - Save received files to disk
|
||||
1. **Add usernames** - Add a username to each message
|
||||
2. **Private rooms** - Use a secret key instead of a room name
|
||||
3. **File sharing** - Add a command to send files
|
||||
4. **Message history** - Use Hypercore to store messages
|
||||
5. **Direct messages** - Send to specific peers, not everyone
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
You just built a P2P chat app with:
|
||||
- ~50 lines of code
|
||||
- ~90 lines of code
|
||||
- No servers
|
||||
- Offline-first by default
|
||||
- Encrypted by default
|
||||
- Peer-to-peer discovery
|
||||
- No accounts
|
||||
- No registration
|
||||
- Direct peer-to-peer communication
|
||||
|
||||
This is the power of Holepunch!
|
||||
This is the power of Hyperswarm - real P2P networking with just a few lines of code!
|
||||
|
||||
---
|
||||
|
||||
@@ -531,4 +481,4 @@ Now that you've built your first app, continue your journey:
|
||||
|
||||
---
|
||||
|
||||
*🎉 Congratulations! You just built a P2P app!*
|
||||
*🎉 Congratulations! You just built a real P2P app!*
|
||||
|
||||
Reference in New Issue
Block a user