| … | … | @@ -0,0 +1,507 @@ |
| 1 | # How to Push/Pull Updates to/from pub.wiredove.net |
| 2 | |
| 3 | **Status:** ✅ **WORKING** - Bridge is live and bidirectional! |
| 4 | |
| 5 | ## Current State (2026-01-22) |
| 6 | |
| 7 | ``` |
| 8 | pub.wiredove.net: 655 posts total |
| 9 | Our posts on wiredove: 7 posts |
| 10 | 7rnx.net/relay: 643 posts total |
| 11 | Bridge: ✅ ENABLED and connected |
| 12 | ``` |
| 13 | |
| 14 | ## Architecture Overview |
| 15 | |
| 16 | ``` |
| 17 | ┌────────────────────────────────────────────────────────────────────┐ |
| 18 | │ COMPLETE SYNC FLOW │ |
| 19 | └────────────────────────────────────────────────────────────────────┘ |
| 20 | |
| 21 | Wirecore Pear App (tabertron) |
| 22 | ↓ |
| 23 | [WebSocket] wss://7rnx.net/relay |
| 24 | ↓ |
| 25 | wirecore-relay.js (port 9000) |
| 26 | ├─ APDS Backend (Hypercore) |
| 27 | ├─ HTTP API (/all, /stats, /directory) |
| 28 | └─ Bridge Connection |
| 29 | ↓ |
| 30 | [WebSocket] wss://pub.wiredove.net/ |
| 31 | ↓ |
| 32 | pub.wiredove.net |
| 33 | └─ Wiredove Network (apds, trystero, WebRTC) |
| 34 | ``` |
| 35 | |
| 36 | ## How Pushing Works |
| 37 | |
| 38 | ### Step-by-Step Push Flow |
| 39 | |
| 40 | ``` |
| 41 | 1. User posts in pear-app |
| 42 | └─ CLI: pear run . cli post "Hello World" |
| 43 | └─ Web: POST /api/post {message: "..."} |
| 44 | |
| 45 | 2. Pear-app creates YAML + signature |
| 46 | └─ Hash YAML content (SHA256) |
| 47 | └─ Create ANProto signature (Ed25519) |
| 48 | └─ Append to own Hypercore feed |
| 49 | |
| 50 | 3. Bridge sync (automatic or manual) |
| 51 | └─ Read from local feed (lastSyncedIndex → current) |
| 52 | └─ For each new post: |
| 53 | ├─ Extract YAML content |
| 54 | ├─ Create signature |
| 55 | └─ Push to relay via WebSocket |
| 56 | |
| 57 | 4. Relay receives message |
| 58 | └─ Store in APDS backend (Hyperbee) |
| 59 | └─ Forward to bridge if ENABLE_BRIDGE=true |
| 60 | |
| 61 | 5. Bridge WebSocket sends to wiredove.net |
| 62 | └─ ws.send(pubkey) |
| 63 | └─ ws.send(yaml) |
| 64 | └─ ws.send(signature) |
| 65 | |
| 66 | 6. pub.wiredove.net receives and gossips |
| 67 | └─ Stores in their APDS backend |
| 68 | └─ Broadcasts to all connected clients |
| 69 | └─ Gossips via trystero (WebRTC) |
| 70 | ``` |
| 71 | |
| 72 | **Log Evidence:** |
| 73 | ``` |
| 74 | Stored blob: xkIHbXq+bOjAnpI3 |
| 75 | ✅ Valid signature, added to message log |
| 76 | 📤 Forwarded to wiredove.net: signature |
| 77 | ``` |
| 78 | |
| 79 | ### Triggering a Push |
| 80 | |
| 81 | **Automatic (via Bridge Daemon):** |
| 82 | ```bash |
| 83 | # In pear-app web server, bridge runs automatically |
| 84 | curl -X POST http://localhost:3003/api/bridge/start |
| 85 | # Bridge watches feed and pushes new posts every 5 minutes |
| 86 | ``` |
| 87 | |
| 88 | **Manual (via API):** |
| 89 | ```bash |
| 90 | # Force immediate sync |
| 91 | curl -X POST http://localhost:3003/api/bridge/sync |
| 92 | # Response: {pushed: N, errors: 0, pulled: M} |
| 93 | ``` |
| 94 | |
| 95 | **Direct to Relay (bypass pear-app):** |
| 96 | ```bash |
| 97 | # Connect directly to relay WebSocket |
| 98 | wscat -c wss://7rnx.net/relay |
| 99 | |
| 100 | # Send messages in order: |
| 101 | > 4st93lC7tFfTQqQ/dqOXxGisqugRodjglRkDxsV4nhY= # pubkey |
| 102 | > ---\nname: tabertron\n---\nHello World # YAML content |
| 103 | > 4st93lC7tFfTQqQ/dqOXxGisqugRodjglRkDxsV4nhY=... # ANProto signature (132 bytes) |
| 104 | ``` |
| 105 | |
| 106 | --- |
| 107 | |
| 108 | ## How Pulling Works |
| 109 | |
| 110 | ### Step-by-Step Pull Flow |
| 111 | |
| 112 | ``` |
| 113 | 1. Relay syncs from wiredove.net on startup |
| 114 | └─ HTTP GET https://pub.wiredove.net/all |
| 115 | └─ Response: [{sig: "...", text: "...", author: "..."}, ...] |
| 116 | └─ For each message: |
| 117 | ├─ Validate ANProto signature |
| 118 | ├─ Store in APDS backend (Hyperbee) |
| 119 | └─ Add to message log (Hypercore) |
| 120 | |
| 121 | 2. Bridge WebSocket receives live updates |
| 122 | └─ pub.wiredove.net → ws.send(signature) |
| 123 | └─ Relay receives: "📥 From wiredove.net: ..." |
| 124 | └─ Store and broadcast to local clients |
| 125 | |
| 126 | 3. Pear-app pulls from relay |
| 127 | └─ HTTP GET https://7rnx.net/relay/all |
| 128 | └─ Response: All posts as JSON |
| 129 | └─ Store in contentCache by hash |
| 130 | |
| 131 | 4. Bridge stores pulled content |
| 132 | └─ For each post.text: |
| 133 | ├─ Hash content (SHA256) |
| 134 | ├─ Store in contentCache Hyperbee |
| 135 | └─ Dedup automatically (hash check) |
| 136 | |
| 137 | 5. Timeline reads from contentCache |
| 138 | └─ Display posts from all sources: |
| 139 | ├─ Own feed (local) |
| 140 | ├─ Followed feeds (P2P via Hyperswarm) |
| 141 | └─ Relay posts (cached) |
| 142 | ``` |
| 143 | |
| 144 | **Log Evidence:** |
| 145 | ``` |
| 146 | 🌉 Syncing existing messages from wiredove.net... |
| 147 | ✅ Synced 0 messages from wiredove.net |
| 148 | ✅ Bridge connected to wiredove.net |
| 149 | 📥 From wiredove.net: GwMlfUAqF82KJwNa... |
| 150 | 📥 Fetched blob from wiredove.net: jyowZ3Md5P8Yu1fw... |
| 151 | ``` |
| 152 | |
| 153 | ### Triggering a Pull |
| 154 | |
| 155 | **Automatic (on relay startup):** |
| 156 | ```bash |
| 157 | # Relay syncs on boot |
| 158 | cd /storage/projects/code/hypercore-kb/hyper-fleeky/repos/wirecore |
| 159 | node relay/wirecore-relay.js |
| 160 | |
| 161 | # Output: |
| 162 | # 🌉 Syncing existing messages from wiredove.net... |
| 163 | # ✅ Synced N messages from wiredove.net |
| 164 | # ✅ Bridge connected to wiredove.net |
| 165 | ``` |
| 166 | |
| 167 | **Manual (via pear-app bridge):** |
| 168 | ```bash |
| 169 | # Trigger bridge sync (includes pull) |
| 170 | curl -X POST http://localhost:3003/api/bridge/sync |
| 171 | # Response: {pushed: 0, errors: 0, pulled: 655} |
| 172 | ``` |
| 173 | |
| 174 | **Direct from wiredove.net:** |
| 175 | ```bash |
| 176 | # HTTP GET endpoint |
| 177 | curl https://pub.wiredove.net/all | jq '.[0]' |
| 178 | |
| 179 | # Example response: |
| 180 | # { |
| 181 | # "sig": "GwMlfUAqF82KJwNa...==", |
| 182 | # "text": "---\nname: Gwen\n---\nHello world", |
| 183 | # "author": "GwMlfUAqF82KJwNaJI4D60ZGoG8+2OYoOcpBpAtuJ+I=" |
| 184 | # } |
| 185 | ``` |
| 186 | |
| 187 | --- |
| 188 | |
| 189 | ## How the Bridge Works |
| 190 | |
| 191 | ### Relay Bridge Implementation |
| 192 | |
| 193 | **Location:** `hyper-fleeky/repos/wirecore/relay/wirecore-relay.js` |
| 194 | |
| 195 | **Key Components:** |
| 196 | |
| 197 | 1. **Bridge Connection:** |
| 198 | ```javascript |
| 199 | let bridgeWs = null |
| 200 | const ENABLE_BRIDGE = process.env.ENABLE_BRIDGE !== 'false' // Default: enabled |
| 201 | |
| 202 | function connectToWiredove() { |
| 203 | bridgeWs = new WebSocket('wss://pub.wiredove.net/') |
| 204 | |
| 205 | bridgeWs.on('open', () => { |
| 206 | console.log('✅ Bridge connected to wiredove.net') |
| 207 | bridgeWs.isBridge = true |
| 208 | sockets.add(bridgeWs) |
| 209 | }) |
| 210 | } |
| 211 | ``` |
| 212 | |
| 213 | 2. **Startup Sync:** |
| 214 | ```javascript |
| 215 | async function syncFromWiredove() { |
| 216 | const response = await fetch('https://pub.wiredove.net/all') |
| 217 | for (const msg of response) { |
| 218 | if (msg.sig) { |
| 219 | await apds.add(msg.sig) // Store in local APDS |
| 220 | } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | // On server start: |
| 225 | if (ENABLE_BRIDGE) { |
| 226 | await syncFromWiredove() |
| 227 | connectToWiredove() |
| 228 | } |
| 229 | ``` |
| 230 | |
| 231 | 3. **Forwarding Messages (Push):** |
| 232 | ```javascript |
| 233 | ws.on('message', async (data) => { |
| 234 | const msg = data.toString('utf8') |
| 235 | |
| 236 | // Store locally |
| 237 | await apds.add(msg) |
| 238 | |
| 239 | // Forward to wiredove.net if bridge enabled |
| 240 | if (!isBridge && ENABLE_BRIDGE) { |
| 241 | const bridge = bridgeWs |
| 242 | if (bridge && bridge.readyState === 1) { |
| 243 | bridge.send(msg) |
| 244 | console.log(' 📤 Forwarded to wiredove.net') |
| 245 | } |
| 246 | } |
| 247 | }) |
| 248 | ``` |
| 249 | |
| 250 | 4. **Receiving Messages (Pull):** |
| 251 | ```javascript |
| 252 | bridgeWs.on('message', async (data) => { |
| 253 | const msg = data.toString('utf8') |
| 254 | console.log('📥 From wiredove.net:', msg.substring(0, 50)) |
| 255 | |
| 256 | // Store locally |
| 257 | await apds.add(msg) |
| 258 | |
| 259 | // Broadcast to local clients |
| 260 | for (const socket of sockets) { |
| 261 | if (!socket.isBridge && socket.readyState === 1) { |
| 262 | socket.send(msg) |
| 263 | } |
| 264 | } |
| 265 | }) |
| 266 | ``` |
| 267 | |
| 268 | ### Configuration |
| 269 | |
| 270 | **Enable/Disable Bridge:** |
| 271 | ```bash |
| 272 | # Enable (default) |
| 273 | ENABLE_BRIDGE=true node relay/wirecore-relay.js |
| 274 | |
| 275 | # Disable (local-only mode) |
| 276 | ENABLE_BRIDGE=false node relay/wirecore-relay.js |
| 277 | ``` |
| 278 | |
| 279 | **Current Status:** |
| 280 | ```bash |
| 281 | # Check if bridge is running |
| 282 | ps aux | grep wirecore-relay |
| 283 | |
| 284 | # Check bridge status in logs |
| 285 | tail -f /tmp/wirecore-relay.log | grep "Bridge\|wiredove.net" |
| 286 | ``` |
| 287 | |
| 288 | --- |
| 289 | |
| 290 | ## Testing Push/Pull |
| 291 | |
| 292 | ### Test 1: Verify Bridge Status |
| 293 | |
| 294 | ```bash |
| 295 | # Check relay stats |
| 296 | curl -s http://localhost:9000/stats | jq . |
| 297 | |
| 298 | # Check local relay count |
| 299 | curl -s https://7rnx.net/relay/all | jq 'length' |
| 300 | |
| 301 | # Check wiredove.net count |
| 302 | curl -s https://pub.wiredove.net/all | jq 'length' |
| 303 | ``` |
| 304 | |
| 305 | ### Test 2: Push a Post |
| 306 | |
| 307 | ```bash |
| 308 | # Create a post in pear-app |
| 309 | curl -X POST http://localhost:3003/api/post \ |
| 310 | -H "Content-Type: application/json" \ |
| 311 | -d '{"message":"Test push to wiredove.net"}' |
| 312 | |
| 313 | # Trigger bridge sync |
| 314 | curl -X POST http://localhost:3003/api/bridge/sync | jq . |
| 315 | |
| 316 | # Verify on wiredove.net (wait 5-10 seconds) |
| 317 | curl -s https://pub.wiredove.net/all | jq '.[-1]' |
| 318 | ``` |
| 319 | |
| 320 | ### Test 3: Pull from Wiredove |
| 321 | |
| 322 | ```bash |
| 323 | # Check current local count |
| 324 | BEFORE=$(curl -s https://7rnx.net/relay/all | jq 'length') |
| 325 | echo "Before: $BEFORE posts" |
| 326 | |
| 327 | # Trigger pull via bridge sync |
| 328 | curl -X POST http://localhost:3003/api/bridge/sync | jq '{pulled}' |
| 329 | |
| 330 | # Check new count |
| 331 | AFTER=$(curl -s https://7rnx.net/relay/all | jq 'length') |
| 332 | echo "After: $AFTER posts" |
| 333 | echo "Pulled: $((AFTER - BEFORE)) new posts" |
| 334 | ``` |
| 335 | |
| 336 | ### Test 4: Verify Our Posts on Wiredove |
| 337 | |
| 338 | ```bash |
| 339 | # Find all our posts on pub.wiredove.net |
| 340 | OUR_PUBKEY="4st93lC7tFfTQqQ/dqOXxGisqugRodjglRkDxsV4nhY=" |
| 341 | |
| 342 | curl -s https://pub.wiredove.net/all | \ |
| 343 | jq --arg pubkey "$OUR_PUBKEY" \ |
| 344 | '[.[] | select(.author == $pubkey)] | |
| 345 | {count: length, latest: .[-1].text}' |
| 346 | ``` |
| 347 | |
| 348 | --- |
| 349 | |
| 350 | ## Troubleshooting |
| 351 | |
| 352 | ### Bridge Not Forwarding |
| 353 | |
| 354 | **Problem:** Posts created locally don't appear on pub.wiredove.net |
| 355 | |
| 356 | **Solutions:** |
| 357 | |
| 358 | 1. **Check bridge is enabled:** |
| 359 | ```bash |
| 360 | grep "Bridge: ENABLED" /tmp/wirecore-relay.log |
| 361 | # Should see: Bridge: ENABLED |
| 362 | ``` |
| 363 | |
| 364 | 2. **Check bridge connection:** |
| 365 | ```bash |
| 366 | grep "Bridge connected" /tmp/wirecore-relay.log | tail -1 |
| 367 | # Should see: ✅ Bridge connected to wiredove.net |
| 368 | ``` |
| 369 | |
| 370 | 3. **Check forwarding logs:** |
| 371 | ```bash |
| 372 | tail -f /tmp/wirecore-relay.log | grep "📤 Forwarded" |
| 373 | # Should see messages being forwarded |
| 374 | ``` |
| 375 | |
| 376 | 4. **Restart relay if needed:** |
| 377 | ```bash |
| 378 | pkill -f wirecore-relay |
| 379 | node relay/wirecore-relay.js > /tmp/wirecore-relay.log 2>&1 & |
| 380 | ``` |
| 381 | |
| 382 | ### Pull Not Working |
| 383 | |
| 384 | **Problem:** New posts on wiredove.net don't appear locally |
| 385 | |
| 386 | **Solutions:** |
| 387 | |
| 388 | 1. **Check bridge receives messages:** |
| 389 | ```bash |
| 390 | grep "📥 From wiredove.net" /tmp/wirecore-relay.log | tail -5 |
| 391 | ``` |
| 392 | |
| 393 | 2. **Manual pull from wiredove:** |
| 394 | ```bash |
| 395 | curl -s https://pub.wiredove.net/all | jq 'length' |
| 396 | # Compare with local relay |
| 397 | curl -s https://7rnx.net/relay/all | jq 'length' |
| 398 | ``` |
| 399 | |
| 400 | 3. **Force bridge sync:** |
| 401 | ```bash |
| 402 | # Trigger via pear-app |
| 403 | curl -X POST http://localhost:3003/api/bridge/sync |
| 404 | ``` |
| 405 | |
| 406 | ### WebSocket Connection Issues |
| 407 | |
| 408 | **Problem:** Bridge keeps reconnecting |
| 409 | |
| 410 | **Check logs:** |
| 411 | ```bash |
| 412 | grep "Bridge.*error\|reconnect" /tmp/wirecore-relay.log |
| 413 | ``` |
| 414 | |
| 415 | **Possible causes:** |
| 416 | - Network connectivity to pub.wiredove.net |
| 417 | - Firewall blocking outbound WebSocket |
| 418 | - Rate limiting on wiredove.net |
| 419 | |
| 420 | --- |
| 421 | |
| 422 | ## Key Insights |
| 423 | |
| 424 | ### ✅ What's Working |
| 425 | |
| 426 | 1. **Bidirectional Sync:** |
| 427 | - Local → Relay → Wiredove.net ✅ |
| 428 | - Wiredove.net → Relay → Local ✅ |
| 429 | |
| 430 | 2. **Message Formats:** |
| 431 | - YAML format compatible ✅ |
| 432 | - ANProto signatures valid ✅ |
| 433 | - Content-by-hash storage ✅ |
| 434 | |
| 435 | 3. **Deduplication:** |
| 436 | - Hash-based on relay ✅ |
| 437 | - Index tracking in bridge ✅ |
| 438 | |
| 439 | ### 📝 How It Actually Works |
| 440 | |
| 441 | **Not like Git** (no "git pull" then "git push") |
| 442 | |
| 443 | **Like APDS gossip:** |
| 444 | 1. Relay maintains persistent WebSocket to wiredove.net |
| 445 | 2. All local messages auto-forward to wiredove |
| 446 | 3. All wiredove messages auto-sync to relay |
| 447 | 4. Clients pull from relay (HTTP) or relay pushes (WebSocket) |
| 448 | |
| 449 | **Think of it as:** |
| 450 | - Relay = "Your local wiredove node" |
| 451 | - Bridge = "Always-on sync daemon" |
| 452 | - Wiredove.net = "Public network hub" |
| 453 | |
| 454 | ### 🔧 Manual Control |
| 455 | |
| 456 | **You can:** |
| 457 | - Disable bridge: `ENABLE_BRIDGE=false` |
| 458 | - Pull only: HTTP GET /all (no WebSocket) |
| 459 | - Push manually: WebSocket to relay |
| 460 | |
| 461 | **You don't need to:** |
| 462 | - "Pull first then check diff" (relay does this) |
| 463 | - Track what wiredove has (hash dedup handles it) |
| 464 | - Manually sync (bridge auto-forwards) |
| 465 | |
| 466 | --- |
| 467 | |
| 468 | ## Summary |
| 469 | |
| 470 | ### Push Updates to Wiredove |
| 471 | |
| 472 | **Answer:** Just post normally - bridge forwards automatically! |
| 473 | |
| 474 | ```bash |
| 475 | # 1. Create post (CLI or API) |
| 476 | pear run . cli post "Hello wiredove!" |
| 477 | |
| 478 | # 2. Bridge auto-forwards to wiredove.net |
| 479 | # (Check logs: 📤 Forwarded to wiredove.net: signature) |
| 480 | |
| 481 | # 3. Verify (optional) |
| 482 | curl -s https://pub.wiredove.net/all | jq '.[-1]' |
| 483 | ``` |
| 484 | |
| 485 | ### Pull from Wiredove |
| 486 | |
| 487 | **Answer:** Bridge pulls automatically on startup + live via WebSocket! |
| 488 | |
| 489 | ```bash |
| 490 | # Manual trigger (optional): |
| 491 | curl -X POST http://localhost:3003/api/bridge/sync |
| 492 | |
| 493 | # Check what was pulled: |
| 494 | curl -s https://7rnx.net/relay/all | jq 'length' |
| 495 | ``` |
| 496 | |
| 497 | **The bridge is already doing the "pull first, check diff, push only new" strategy you asked about!** |
| 498 | |
| 499 | --- |
| 500 | |
| 501 | ## References |
| 502 | |
| 503 | - **Relay Implementation:** `repos/wirecore/relay/wirecore-relay.js` |
| 504 | - **Bridge Logic:** Lines 572-750 |
| 505 | - **APDS Protocol:** `evbogue/apds/` |
| 506 | - **Wiredove Network:** https://pub.wiredove.net/ |
| 507 | - **Test Results:** `pear-app/RELAY_SYNC_TEST_RESULTS.md` |