| … | … | @@ -0,0 +1,347 @@ |
| 1 | # Wirecore Pear App Architecture |
| 2 | |
| 3 | ## Current Status: Architectural Deviation |
| 4 | |
| 5 | **Critical Finding**: The Pear app implementation has deviated from the original Wirecore design goals. |
| 6 | |
| 7 | --- |
| 8 | |
| 9 | ## Original Wirecore Design (lib/core/wirecore.js) |
| 10 | |
| 11 | ### Core Principles |
| 12 | |
| 13 | 1. **ANProto Signatures**: Messages are authenticated with portable ed25519 signatures |
| 14 | 2. **Content-by-Hash Storage**: Actual content stored separately from signatures |
| 15 | 3. **Hypercore Efficiency**: Only signatures (132 bytes) replicated via P2P |
| 16 | 4. **Lazy Content Fetch**: Content retrieved on-demand via ContentSync |
| 17 | |
| 18 | ### Message Flow (Original Design) |
| 19 | |
| 20 | ```javascript |
| 21 | // 1. Create YAML message with metadata |
| 22 | const yaml = YAMLHelper.create({ |
| 23 | name: 'fleeky', |
| 24 | image: 'hash-of-profile-pic', |
| 25 | previous: 'hash-of-previous-post' |
| 26 | }, 'Hello world!') |
| 27 | |
| 28 | // 2. Hash the content (SHA256) |
| 29 | const contentHash = ANProto.hashData(yaml) |
| 30 | // → "pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=" |
| 31 | |
| 32 | // 3. Store content by hash |
| 33 | await contentStore.put(contentHash, yaml) |
| 34 | // → Writes to storage/content.json |
| 35 | |
| 36 | // 4. Sign the hash |
| 37 | const signature = ANProto.signHash(contentHash, keypair) |
| 38 | // → "BSY7/er4VJIu08o39...wnlzLSoZ1QC7rHJc..." |
| 39 | // → Format: publicKey(44) + signed(timestamp + hash) |
| 40 | |
| 41 | // 5. Append ONLY signature to Hypercore |
| 42 | await myFeed.append(Buffer.from(signature)) |
| 43 | // → Hypercore block contains 132 bytes, not full message! |
| 44 | ``` |
| 45 | |
| 46 | ### Storage Architecture (Original Design) |
| 47 | |
| 48 | ``` |
| 49 | wirecore-storage/ |
| 50 | ├── keypair.json # ANProto ed25519 keypair (132 chars) |
| 51 | ├── content.json # Hash → Content mapping |
| 52 | │ { |
| 53 | │ "pZGm1Av0...": "---\nname: fleeky\n---\nHello world!", |
| 54 | │ "anotherHash...": "..." |
| 55 | │ } |
| 56 | └── feeds/ # Corestore |
| 57 | └── cores/ |
| 58 | └── {discovery-key}/ # Hypercore blocks (signatures only!) |
| 59 | ├── bitfield |
| 60 | ├── data # Contains ANProto signatures |
| 61 | └── tree |
| 62 | ``` |
| 63 | |
| 64 | ### Why This Design? |
| 65 | |
| 66 | **Efficient P2P Replication**: |
| 67 | - Signature: 132 bytes |
| 68 | - Full message with image: Could be KB/MB |
| 69 | - Replicate signatures first, fetch content lazily |
| 70 | |
| 71 | **Content Deduplication**: |
| 72 | - Same content = same hash |
| 73 | - Multiple users can reference same image/text |
| 74 | - Store once, reference many times |
| 75 | |
| 76 | **Wiredove Compatibility**: |
| 77 | - Wiredove uses same ANProto + content-by-hash model |
| 78 | - Wiredove stores in IndexedDB, Wirecore in Hypercore |
| 79 | - Messages portable between systems |
| 80 | |
| 81 | **Protocol Flexibility**: |
| 82 | - ANProto signatures work over ANY transport (HTTP, WebRTC, DHT, USB) |
| 83 | - Not tied to Hypercore's built-in signing |
| 84 | - Can migrate identity between runtimes |
| 85 | |
| 86 | --- |
| 87 | |
| 88 | ## Current Pear App Implementation (pear-app/index.js) |
| 89 | |
| 90 | ### What It Does |
| 91 | |
| 92 | ```javascript |
| 93 | // Create message entry |
| 94 | const entry = { |
| 95 | type: 'post', |
| 96 | text: message, |
| 97 | timestamp: Date.now(), |
| 98 | author: b4a.toString(myFeed.key, 'hex'), |
| 99 | username: username || null |
| 100 | } |
| 101 | |
| 102 | // Store FULL JSON in Hypercore |
| 103 | await myFeed.append(Buffer.from(JSON.stringify(entry))) |
| 104 | // → Hypercore block contains hundreds of bytes! |
| 105 | ``` |
| 106 | |
| 107 | ### Problems with Current Approach |
| 108 | |
| 109 | 1. **No ANProto Usage**: Messages not signed with ANProto protocol |
| 110 | 2. **No Content-by-Hash**: Full messages stored in Hypercore blocks |
| 111 | 3. **Inefficient Replication**: Large blocks slow down P2P sync |
| 112 | 4. **No Wiredove Compatibility**: Can't interoperate with wiredove.net users |
| 113 | 5. **Identity Not Portable**: Relies on Hypercore keypair, not ANProto keypair |
| 114 | 6. **No YAML Format**: Missing name/image/previous metadata structure |
| 115 | |
| 116 | ### Storage Architecture (Current Implementation) |
| 117 | |
| 118 | ``` |
| 119 | wirecore-storage/ (or Pear's by-dkey path) |
| 120 | └── cores/ |
| 121 | ├── identity/ # IdentityKey seed (Keet-style) |
| 122 | ├── my-feed/ # Hypercore with full JSON messages |
| 123 | └── metadata/ # Hyperbee with username, follows |
| 124 | ``` |
| 125 | |
| 126 | **Missing Components**: |
| 127 | - ❌ ANProto keypair.json |
| 128 | - ❌ content.json (content-by-hash storage) |
| 129 | - ❌ ContentSync layer |
| 130 | - ❌ HyperswarmContentTransport |
| 131 | |
| 132 | --- |
| 133 | |
| 134 | ## Why Did This Happen? |
| 135 | |
| 136 | ### Context |
| 137 | |
| 138 | The Pear app was built as a **quick prototype** to test: |
| 139 | - Pear runtime capabilities |
| 140 | - Hypercore/Hyperswarm in Bare.js |
| 141 | - Web interface with WebSockets |
| 142 | - Basic social network features |
| 143 | |
| 144 | ### Trade-offs Made |
| 145 | |
| 146 | ✅ **Gained**: |
| 147 | - Faster initial development |
| 148 | - Simpler code (no ContentSync complexity) |
| 149 | - Direct Hypercore usage |
| 150 | - Working P2P replication |
| 151 | |
| 152 | ❌ **Lost**: |
| 153 | - ANProto compatibility |
| 154 | - Wiredove interoperability |
| 155 | - Efficient replication |
| 156 | - Content deduplication |
| 157 | - Identity portability |
| 158 | |
| 159 | --- |
| 160 | |
| 161 | ## Path Forward |
| 162 | |
| 163 | ### Option 1: Full Refactor (Align with Original Design) |
| 164 | |
| 165 | **Implement**: |
| 166 | 1. Add ANProto signature layer |
| 167 | 2. Implement content-by-hash storage (content.json) |
| 168 | 3. Add ContentSync + HyperswarmContentTransport |
| 169 | 4. Convert messages to YAML format |
| 170 | 5. Store only signatures in Hypercore blocks |
| 171 | |
| 172 | **Benefits**: |
| 173 | - ✅ Wiredove compatibility |
| 174 | - ✅ Efficient P2P replication |
| 175 | - ✅ Content deduplication |
| 176 | - ✅ Portable identity |
| 177 | - ✅ Aligns with original vision |
| 178 | |
| 179 | **Costs**: |
| 180 | - 🔴 Significant refactoring required |
| 181 | - 🔴 Need to migrate existing data |
| 182 | - 🔴 More complex codebase |
| 183 | - 🔴 Breaking change for existing users |
| 184 | |
| 185 | ### Option 2: Hybrid Approach |
| 186 | |
| 187 | **Keep**: |
| 188 | - Current simple Hypercore-only model for Pear app |
| 189 | - Direct feed replication (no ContentSync) |
| 190 | |
| 191 | **Add**: |
| 192 | - ANProto signing layer on top of Hypercore |
| 193 | - Optional YAML metadata in posts |
| 194 | - Gateway bridge for Wiredove compatibility |
| 195 | |
| 196 | **Benefits**: |
| 197 | - ✅ Less refactoring |
| 198 | - ✅ Gradual migration path |
| 199 | - ✅ Can add Wiredove bridge later |
| 200 | |
| 201 | **Costs**: |
| 202 | - ⚠️ Still inefficient for large content |
| 203 | - ⚠️ Partial compatibility only |
| 204 | - ⚠️ Duplicated messages (Hypercore + content.json) |
| 205 | |
| 206 | ### Option 3: Fork/Rename (Accept Divergence) |
| 207 | |
| 208 | **Acknowledge**: |
| 209 | - Pear app is a different implementation |
| 210 | - Name it differently (not "Wirecore") |
| 211 | - Focus on Pear/Hypercore strengths |
| 212 | |
| 213 | **Benefits**: |
| 214 | - ✅ No refactoring needed |
| 215 | - ✅ Clear separation of concerns |
| 216 | - ✅ Can optimize for Pear specifically |
| 217 | |
| 218 | **Costs**: |
| 219 | - 🔴 No Wiredove compatibility |
| 220 | - 🔴 Duplicate effort (two codebases) |
| 221 | - 🔴 Loses ANProto benefits |
| 222 | |
| 223 | --- |
| 224 | |
| 225 | ## Recommendation |
| 226 | |
| 227 | **Start with Option 2 (Hybrid Approach)** for these reasons: |
| 228 | |
| 229 | 1. **Incremental Migration**: Can add ANProto without breaking existing code |
| 230 | 2. **Testing Ground**: Pear app can validate ANProto design for eventual lib/core integration |
| 231 | 3. **User Impact**: Existing Pear app users don't lose data |
| 232 | 4. **Bridge to Wiredove**: Can add relay bridge to wiredove.net for discovery |
| 233 | |
| 234 | ### Immediate Next Steps |
| 235 | |
| 236 | 1. **Document Current State** ✅ (this file) |
| 237 | 2. **Add ANProto Signing Layer**: |
| 238 | - Keep Hypercore storage as-is |
| 239 | - Add ANProto signature verification |
| 240 | - Include signature in message metadata |
| 241 | 3. **Add Content Store**: |
| 242 | - Create content.json for images/large content |
| 243 | - Keep text in Hypercore for now |
| 244 | 4. **Test Wiredove Bridge**: |
| 245 | - Relay can convert between formats |
| 246 | - Directory discovery already working |
| 247 | |
| 248 | --- |
| 249 | |
| 250 | ## Technical Details |
| 251 | |
| 252 | ### ANProto Signature Format |
| 253 | |
| 254 | ``` |
| 255 | Signature (132-176 chars base64): |
| 256 | ┌──────────── 44 chars ────────────┐ ┌────── 88+ chars ──────┐ |
| 257 | publicKey (32 bytes, ed25519) signed(timestamp + hash) |
| 258 | ``` |
| 259 | |
| 260 | **Components**: |
| 261 | - `publicKey`: 32-byte ed25519 public key (base64) |
| 262 | - `timestamp`: 13-digit Unix timestamp in milliseconds |
| 263 | - `hash`: 32-byte SHA256 hash of content (base64) |
| 264 | - `signature`: ed25519 signature of (timestamp + hash) |
| 265 | |
| 266 | **Example**: |
| 267 | ``` |
| 268 | BSY7/er4VJIu08o39NaRAiPY/MAvd7oQhlGCRDABjYU=wnlzLSoZ1QC7rHJcH4qIJJnQQEhKgqS5OEIE+OIUE... |
| 269 | └─────────────────┬─────────────────┘ └───────────────┬───────────────────────┘ |
| 270 | Public Key Signature Blob |
| 271 | ``` |
| 272 | |
| 273 | ### YAML Message Format |
| 274 | |
| 275 | ```yaml |
| 276 | --- |
| 277 | name: fleeky |
| 278 | image: pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4= |
| 279 | previous: anotherHashOfPreviousPost... |
| 280 | reply: hashOfPostBeingRepliedTo... |
| 281 | --- |
| 282 | This is the actual post text content. |
| 283 | It can be multiple lines and contain any text. |
| 284 | ``` |
| 285 | |
| 286 | **Metadata Fields**: |
| 287 | - `name`: Display name (optional, can change) |
| 288 | - `image`: Hash of profile picture (optional) |
| 289 | - `previous`: Hash of user's previous post (for thread ordering) |
| 290 | - `reply`: Hash of post being replied to (for conversations) |
| 291 | - `repost`: Hash of post being reposted/boosted (for amplification) |
| 292 | |
| 293 | ### Content-by-Hash Storage |
| 294 | |
| 295 | **Format (content.json)**: |
| 296 | ```json |
| 297 | { |
| 298 | "pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=": { |
| 299 | "type": "text", |
| 300 | "data": "---\nname: fleeky\n---\nHello world!" |
| 301 | }, |
| 302 | "imageHash123...": { |
| 303 | "type": "image", |
| 304 | "mime": "image/jpeg", |
| 305 | "data": "base64-encoded-image-data..." |
| 306 | } |
| 307 | } |
| 308 | ``` |
| 309 | |
| 310 | **Operations**: |
| 311 | ```javascript |
| 312 | // Store |
| 313 | await contentStore.put(hash, content) |
| 314 | |
| 315 | // Retrieve |
| 316 | const content = await contentStore.get(hash) |
| 317 | if (!content) { |
| 318 | // Request from peers via ContentSync |
| 319 | content = await contentSync.fetch(hash) |
| 320 | } |
| 321 | ``` |
| 322 | |
| 323 | --- |
| 324 | |
| 325 | ## References |
| 326 | |
| 327 | **Original Design Documentation**: |
| 328 | - `docs/CLAUDE-SKILLS.md` - Semantic keyword index |
| 329 | - `docs/claude/wirecore-skills.md` - Complete architecture reference |
| 330 | - `docs/kb/architecture/TWO_IDENTITY_SYSTEMS.md` - Dual identity model |
| 331 | - `docs/kb/architecture/WIREDOVE_COMPATIBILITY.md` - Wiredove bridge design |
| 332 | |
| 333 | **Implementation**: |
| 334 | - `lib/core/wirecore.js` - Original implementation |
| 335 | - `lib/core/anproto.js` - ANProto signature protocol |
| 336 | - `lib/core/content-sync.js` - Content-by-hash fetching |
| 337 | - `lib/core/content-store.js` - Hash → Content storage |
| 338 | |
| 339 | **Current Pear App**: |
| 340 | - `pear-app/index.js` - Current implementation |
| 341 | - `pear-app/STORAGE_MIGRATION.md` - Storage migration guide (for current model) |
| 342 | |
| 343 | --- |
| 344 | |
| 345 | **Status**: Documentation complete, awaiting architectural decision |
| 346 | **Date**: 2026-01-21 |
| 347 | **Author**: Claude Sonnet 4.5 |