Formatting fixes
This commit is contained in:
@@ -1,7 +1,63 @@
|
||||
# Hyperbee Chat Test
|
||||
|
||||
## Test Results
|
||||
Local writer: puts increment version, scans work.
|
||||
No P2P connections on localhost (expected; DHT bootstrap/NAT sim needs multi-host).
|
||||
Code P2P-ready: fixed topic chat room, deterministic named core 'test-bee'.
|
||||
Core key deterministic via corestore.get('test-bee') (run for hex).\n\nP2P chat prototype using Hyperbee (KV on Hypercore) + Hyperswarm.\n\n## Features\n- Timestamp keys for msgs\n- JSON values {msg, peer, ts}\n- Swarm discovery on fixed topic\n- Writer appends every 3s\n- Reader scans recent 2min msgs every 5s\n- Separate storages, named core 'test-bee' (deterministic key)\n\n## Run\n```\nnpm i\nnode index.js writer # Appends msgs\nnode index.js reader # Scans recent\n```\n\n## Expected\n- Core ready, key logged\n- Writer: Puts increment version\n- Reader: Local scan (0 until replicate)\n- On P2P conn: 'New connection', reader sees writer msgs\n\nLocalhost DHT: Slow/no conn expected; verify code + local ops.\n\nTest: Runs 30s auto-exit.
|
||||
A minimal P2P chat prototype built with **Hyperbee** (sorted key-value store on Hypercore) + **Hyperswarm** for peer discovery and replication.
|
||||
|
||||
## Features
|
||||
|
||||
- Timestamp-based keys (zero-padded for correct lexicographic ordering)
|
||||
- JSON values: `{ msg, peer }`
|
||||
- Fixed swarm topic for easy peer discovery
|
||||
- Writer appends a new message every 3 seconds
|
||||
- Reader scans and displays messages from the last 2 minutes every 5 seconds
|
||||
- Uses `Corestore` with a **named core** (`test-bee`) → same public key on every run
|
||||
- Proper replication of the entire Corestore on every connection (best practice)
|
||||
- Auto-exits after 30 seconds for easy testing
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
npm install hyperswarm corestore hyperbee b4a
|
||||
```
|
||||
|
||||
Run in two separate terminals:
|
||||
|
||||
```bash
|
||||
# Terminal 1 - Writer
|
||||
node index.js writer
|
||||
|
||||
# Terminal 2 - Reader
|
||||
node index.js reader
|
||||
```
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
- Both instances log the **same core public key** (deterministic thanks to named core)
|
||||
- Writer: Successfully puts messages and increments the Hyperbee version
|
||||
- Reader: Shows local scan results (initially 0 messages until replication)
|
||||
- When peers connect over Hyperswarm:
|
||||
- "New P2P connection" message appears
|
||||
- Reader starts seeing the writer's messages in real time
|
||||
|
||||
**Note on localhost testing**:
|
||||
Connections on the same machine can be slow or fail due to DHT/bootstrap/NAT behavior. The code is fully P2P-ready — test with two different machines (or different networks) for reliable replication. Local operations (write + scan) work immediately.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `writer-storage/` — persistent storage for the writer
|
||||
- `reader-storage/` — persistent storage for the reader
|
||||
- Both use the same named Hypercore (`test-bee`)
|
||||
|
||||
## How It Works
|
||||
|
||||
- **Discovery**: Both peers join the same SHA-256 hashed topic
|
||||
- **Replication**: `corestore.replicate(conn)` on every incoming/outgoing connection
|
||||
- **Data Model**: Hyperbee with UTF-8 keys and JSON values
|
||||
- **Ordering**: Messages are naturally sorted by timestamp
|
||||
|
||||
---
|
||||
|
||||
**Test duration**: Both modes automatically stop after 30 seconds.
|
||||
|
||||
Feel free to extend this into a full chat app by adding user input, live subscriptions (`db.createReadStream({ live: true })`), or message deletion.
|
||||
|
||||
Happy hacking!
|
||||
|
||||
@@ -1 +1,130 @@
|
||||
#!/usr/bin/env node\nconst Hyperswarm = require('hyperswarm')\nconst Corestore = require('corestore')\nconst Hyperbee = require('hyperbee')\nconst process = require('process')\n\nconst mode = process.argv[2] || 'reader'\nconst storage = `./${mode}-storage`\n\nconsole.log(`Mode: ${mode}, storage: ${storage}`)\n\nasync function main() {\n const corestore = new Corestore(storage)\n const core = corestore.get('test-bee')\n console.log('Core instance created')\n await core.ready()\n console.log('Core ready, key length:', core.key ? core.key.length : 'no key')\n const db = new Hyperbee(core, { keyEncoding: 'utf-8', valueEncoding: 'json' })\n console.log('Hyperbee instance created')\n await db.ready()\n console.log('Hyperbee ready, version:', db.version)\n console.log('Core key:', core.key.toString('hex'))\n console.log('Initial Bee version:', db.version)\n\n const swarm = new Hyperswarm()\n const topic = Buffer.alloc(32).fill('hyperbee-chat-test')\n swarm.join(topic, { server: true, client: true })\n swarm.on('connection', (conn, info) => {\n console.log('New P2P connection:', !!(info.client), !!(info.server))\n core.replicate(conn)\n })\n swarm.on('updated', () => {\n console.log(`Swarm has ${swarm.connections.size} connections`)\n })\n\n if (mode === 'writer') {\n let count = 0\n const interval = setInterval(async () => {\n try {\n const ts = Date.now().toString()\n const msg = { msg: `Hello from writer #${count} at ${new Date().toISOString()}`, peer: 'writer' }\n await db.put(ts, msg)\n console.log(`Put msg #${count + 1} at key ${ts.slice(-8)}`)\n console.log('Bee version:', db.version)\n count++\n } catch (err) {\n console.error('Put error:', err.message)\n }\n }, 3000)\n\n setTimeout(() => {\n console.log('Test complete')\n clearInterval(interval)\n process.exit(0)\n }, 30000).unref()\n } else {\n // reader\n const readInterval = setInterval(async () => {\n try {\n const now = Date.now()\n const fromTs = (now - 120000).toString() // last 2 min\n console.log('Scanning msgs from', fromTs.slice(-8), 'version:', db.version)\n let found = 0\n for await (const entry of db.createReadStream({ gte: fromTs })) {\n console.log(` ${entry.key.slice(-8)}: ${entry.value.msg} (${entry.value.peer})`)\n found++\n }\n console.log(`Found ${found} recent msgs`)\n } catch (err) {\n console.error('Scan error:', err.message)\n }\n }, 5000)\n\n setTimeout(() => {\n console.log('Test complete')\n clearInterval(readInterval)\n process.exit(0)\n }, 30000).unref()\n }\n}\n\nmain().catch(err => {\n console.error('Fatal error:', err)\n process.exit(1)\n})\n
|
||||
#!/usr/bin/env node
|
||||
|
||||
const Hyperswarm = require('hyperswarm')
|
||||
const Corestore = require('corestore')
|
||||
const Hyperbee = require('hyperbee')
|
||||
const crypto = require('crypto')
|
||||
const process = require('process')
|
||||
const b4a = require('b4a') // optional but recommended for buffer handling
|
||||
|
||||
const mode = process.argv[2] || 'reader'
|
||||
const storage = `./${mode}-storage`
|
||||
|
||||
console.log(`Mode: ${mode}, storage: ${storage}`)
|
||||
|
||||
async function main() {
|
||||
const corestore = new Corestore(storage)
|
||||
// Use a named core so both writer and reader use the same logical core
|
||||
const core = corestore.get({ name: 'test-bee' })
|
||||
|
||||
console.log('Core instance created')
|
||||
await core.ready()
|
||||
console.log('Core ready, key:', core.key ? core.key.toString('hex') : 'no key yet')
|
||||
|
||||
const db = new Hyperbee(core, {
|
||||
keyEncoding: 'utf-8',
|
||||
valueEncoding: 'json'
|
||||
})
|
||||
|
||||
console.log('Hyperbee instance created')
|
||||
await db.ready()
|
||||
console.log('Hyperbee ready, version:', db.version)
|
||||
console.log('Core public key:', core.key.toString('hex'))
|
||||
|
||||
// === Swarm setup ===
|
||||
const swarm = new Hyperswarm()
|
||||
|
||||
// Create a stable 32-byte topic (discovery key)
|
||||
const topic = crypto.createHash('sha256')
|
||||
.update('hyperbee-chat-test')
|
||||
.digest()
|
||||
|
||||
console.log('Joining swarm with topic:', topic.toString('hex').slice(0, 16) + '...')
|
||||
|
||||
const discovery = swarm.join(topic, { server: true, client: true })
|
||||
await discovery.flushed() // wait until announced
|
||||
|
||||
swarm.on('connection', (conn, info) => {
|
||||
const isClient = !!info.client
|
||||
const isServer = !!info.server
|
||||
console.log(`New P2P connection: ${isClient ? 'client' : ''} ${isServer ? 'server' : ''}`)
|
||||
|
||||
// Replicate the entire corestore (best practice)
|
||||
corestore.replicate(conn)
|
||||
})
|
||||
|
||||
swarm.on('updated', () => {
|
||||
console.log(`Swarm connections: ${swarm.connections.size}`)
|
||||
})
|
||||
|
||||
// === Writer mode ===
|
||||
if (mode === 'writer') {
|
||||
let count = 0
|
||||
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const ts = Date.now().toString().padStart(20, '0') // zero-pad for correct sorting
|
||||
const msg = {
|
||||
msg: `Hello from writer #${count} at ${new Date().toISOString()}`,
|
||||
peer: 'writer'
|
||||
}
|
||||
|
||||
await db.put(ts, msg)
|
||||
console.log(`✓ Put #${count} | key: ${ts.slice(-8)} | version: ${db.version}`)
|
||||
count++
|
||||
} catch (err) {
|
||||
console.error('Put error:', err.message)
|
||||
}
|
||||
}, 3000)
|
||||
|
||||
// Auto-stop after 30s
|
||||
setTimeout(() => {
|
||||
console.log('Writer test complete')
|
||||
clearInterval(interval)
|
||||
shutdown()
|
||||
}, 30000).unref()
|
||||
|
||||
} else {
|
||||
// === Reader mode ===
|
||||
const readInterval = setInterval(async () => {
|
||||
try {
|
||||
const now = Date.now()
|
||||
const fromTs = (now - 120000).toString().padStart(20, '0') // last 2 minutes
|
||||
|
||||
console.log(`Scanning from ${fromTs.slice(-8)} (version: ${db.version})`)
|
||||
|
||||
let found = 0
|
||||
for await (const entry of db.createReadStream({
|
||||
gte: fromTs,
|
||||
limit: 50 // prevent flooding the console
|
||||
})) {
|
||||
console.log(` ${entry.key.slice(-8)}: ${entry.value.msg} (${entry.value.peer})`)
|
||||
found++
|
||||
}
|
||||
console.log(`→ Found ${found} recent messages`)
|
||||
} catch (err) {
|
||||
console.error('Scan error:', err.message)
|
||||
}
|
||||
}, 5000)
|
||||
|
||||
setTimeout(() => {
|
||||
console.log('Reader test complete')
|
||||
clearInterval(readInterval)
|
||||
shutdown()
|
||||
}, 30000).unref()
|
||||
}
|
||||
|
||||
// Graceful shutdown
|
||||
async function shutdown() {
|
||||
console.log('Shutting down...')
|
||||
await swarm.destroy()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
process.on('SIGINT', shutdown)
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Fatal error:', err)
|
||||
process.exit(1)
|
||||
})
|
||||
Reference in New Issue
Block a user