|
1 | 1 | # Technical Architecture |
2 | 2 |
|
3 | | -Hypermark is designed as a local-first, privacy-focused Progressive Web App (PWA). It ensures that all bookmark data is encrypted on the device and synchronized directly between devices without ever exposing plaintext content to a server. |
| 3 | +Hypermark is a local-first, privacy-focused Progressive Web App (PWA). All bookmark data is encrypted on the device and synchronized via a hybrid system: real-time P2P sync (WebRTC) and asynchronous cloud sync (Nostr relays). No server ever sees plaintext content. |
4 | 4 |
|
5 | 5 | ## System Overview |
6 | 6 |
|
7 | | -The following diagram illustrates the high-level data flow and component interaction: |
8 | | - |
9 | 7 | ```text |
10 | | - ┌─────────────────────────────────────────────────────────┐ |
11 | | - │ Browser / PWA Context │ |
12 | | - │ │ |
13 | | - │ ┌──────────────────┐ ┌─────────────────────┐ │ |
14 | | - │ │ Preact UI Layer │◄────────►│ Service Layer │ │ |
15 | | - │ └──────────────────┘ │ (Business Logic) │ │ |
16 | | - │ └──────────┬──────────┘ │ |
17 | | - │ │ │ |
18 | | - │ ┌─────────────────────────┼─────────────┤ |
19 | | - │ │ │ │ |
20 | | - │ ┌────────▼────────┐ ┌────────▼──────────┐ │ |
21 | | - │ │ Yjs Document │ │ Web Crypto API │ │ |
22 | | - │ │ (CRDT) │ │ (AES-GCM / ECDH) │ │ |
23 | | - │ └────────┬────────┘ └───────────────────┘ │ |
24 | | - │ │ │ |
25 | | - │ ┌────────▼────────┐ ┌───────────────────┐ │ |
26 | | - │ │ IndexedDB │ │ WebRTC (y-webrtc)│ │ |
27 | | - │ │ (Persistence) │ │ (P2P Sync) │ │ |
28 | | - │ └─────────────────┘ └─────────┬─────────┘ │ |
29 | | - │ │ │ |
30 | | - └────────────────────────────────────────────┼────────────┘ |
31 | | - │ |
32 | | - ┌────────▼────────┐ |
33 | | - │ Signaling Server│ |
34 | | - │ (Metadata Only) │ |
35 | | - └─────────────────┘ |
| 8 | + ┌─────────────────────────────────────────────────────────────┐ |
| 9 | + │ Browser / PWA Context │ |
| 10 | + │ │ |
| 11 | + │ ┌──────────────────┐ ┌──────────────────────────┐ │ |
| 12 | + │ │ React UI Layer │◄────────►│ Service Layer │ │ |
| 13 | + │ └──────────────────┘ │ (Business Logic) │ │ |
| 14 | + │ └───────────┬──────────────┘ │ |
| 15 | + │ │ │ |
| 16 | + │ ┌──────────────────────────┼────────────────┤ |
| 17 | + │ │ │ │ |
| 18 | + │ ┌────────▼────────┐ ┌───────▼──────────┐ │ |
| 19 | + │ │ Yjs Document │ │ Web Crypto API │ │ |
| 20 | + │ │ (CRDT) │ │ (AES-GCM / ECDH) │ │ |
| 21 | + │ └────────┬────────┘ └──────────────────┘ │ |
| 22 | + │ │ │ |
| 23 | + │ ┌────────────┼────────────┬──────────────────┐ │ |
| 24 | + │ │ │ │ │ │ |
| 25 | + │ ▼ ▼ ▼ ▼ │ |
| 26 | + │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌────────────┐ │ |
| 27 | + │ │IndexedDB │ │ WebRTC │ │NostrSyncSvc │ │nostr-crypto│ │ |
| 28 | + │ │(persist) │ │(P2P sync)│ │(cloud sync) │ │(secp256k1) │ │ |
| 29 | + │ └──────────┘ └────┬─────┘ └──────┬───────┘ └────────────┘ │ |
| 30 | + │ │ │ │ |
| 31 | + └───────────────────┼──────────────┼─────────────────────────┘ |
| 32 | + │ │ |
| 33 | + ┌────────▼────────┐ │ |
| 34 | + │ Signaling Server│ │ |
| 35 | + │ (WebRTC only) │ │ |
| 36 | + └─────────────────┘ │ |
| 37 | + │ |
| 38 | + ┌─────────────▼─────────────┐ |
| 39 | + │ Nostr Relays │ |
| 40 | + │ (encrypted events only) │ |
| 41 | + │ - relay.damus.io │ |
| 42 | + │ - nos.lol │ |
| 43 | + │ - relay.nostr.band │ |
| 44 | + └───────────────────────────┘ |
36 | 45 | ``` |
37 | 46 |
|
38 | 47 | ## Tech Stack |
39 | 48 |
|
40 | 49 | | Layer | Technology | |
41 | 50 | |-------|------------| |
42 | | -| UI Framework | Preact + Vite | |
| 51 | +| UI Framework | React + Vite | |
43 | 52 | | State Management | Yjs (CRDT) | |
44 | 53 | | Local Storage | IndexedDB (via y-indexeddb) | |
45 | 54 | | P2P Transport | WebRTC Data Channels (via y-webrtc) | |
46 | | -| Encryption | Web Crypto API (AES-256-GCM) | |
47 | | -| Search | MiniSearch (Client-side) | |
48 | | -| Styling | Tailwind CSS + DaisyUI | |
| 55 | +| Cloud Sync | Nostr protocol (kind 30053 events) | |
| 56 | +| Encryption | Web Crypto API (AES-256-GCM) + secp256k1 | |
| 57 | +| Search | MiniSearch (client-side) | |
| 58 | +| Styling | Tailwind CSS v4 + Radix UI primitives | |
| 59 | + |
| 60 | +## Hybrid Sync Architecture |
| 61 | + |
| 62 | +Hypermark uses two complementary sync mechanisms: |
| 63 | + |
| 64 | +### 1. WebRTC P2P Sync (Real-time) |
| 65 | +- Sub-second latency when both devices are online |
| 66 | +- Direct peer-to-peer, no server sees data |
| 67 | +- Uses y-webrtc provider for Yjs |
| 68 | +- Room password derived from LEK via HKDF |
| 69 | + |
| 70 | +### 2. Nostr Cloud Sync (Asynchronous) |
| 71 | +- Works when devices are not online simultaneously |
| 72 | +- Encrypted events stored on decentralized relays |
| 73 | +- Uses parameterized replaceable events (kind 30053) |
| 74 | +- Keypair deterministically derived from LEK |
| 75 | +- 1.5s debounce to batch rapid changes |
| 76 | + |
| 77 | +Both sync to the same Yjs document - changes merge via CRDT. |
49 | 78 |
|
50 | 79 | ## Data Flow |
51 | 80 |
|
52 | 81 | 1. **User Action**: User adds or modifies a bookmark in the UI. |
53 | 82 | 2. **Service Layer**: The `bookmarks.js` service validates the input. |
54 | 83 | 3. **CRDT Update**: The change is applied to the local Yjs document. |
55 | | -4. **Local Persistence**: `y-indexeddb` automatically persists the change to IndexedDB. |
56 | | -5. **P2P Sync**: If other devices are connected via `y-webrtc`, the delta is broadcast over an encrypted WebRTC data channel. |
57 | | -6. **Background Indexing**: `useSearch` hook detects changes in the Yjs document and updates the local MiniSearch index. |
| 84 | +4. **Local Persistence**: `y-indexeddb` automatically persists to IndexedDB. |
| 85 | +5. **P2P Sync**: If devices connected via `y-webrtc`, delta broadcasts immediately. |
| 86 | +6. **Cloud Sync**: `NostrSyncService` publishes encrypted event to Nostr relays (debounced). |
| 87 | +7. **Background Indexing**: `useSearch` hook updates the MiniSearch index. |
58 | 88 |
|
59 | 89 | ## Key Concepts |
60 | 90 |
|
61 | 91 | ### Ledger Encryption Key (LEK) |
62 | | -The LEK is a symmetric AES-256 key generated on the first device. It is used to encrypt all bookmark content. During pairing, the LEK is securely transferred to the new device via an ECDH key exchange. |
| 92 | +The LEK is a symmetric AES-256 key generated on the first device. It encrypts all bookmark content. During pairing, the LEK is securely transferred via ECDH key exchange. |
63 | 93 |
|
64 | | -### Yjs CRDTs |
65 | | -Hypermark uses Yjs to manage its data. CRDTs (Conflict-free Replicated Data Types) ensure that concurrent edits from different devices merge deterministically without a central authority. |
| 94 | +### Derived Keys |
| 95 | +From the LEK, we derive: |
| 96 | +- **Yjs room password** (HKDF) - for WebRTC encryption |
| 97 | +- **Nostr keypair** (secp256k1) - deterministic, same on all devices |
66 | 98 |
|
67 | | -### WebRTC P2P Sync |
68 | | -Devices sync directly with each other. The signaling server is only used to help devices discover each other and negotiate the initial connection. Once the WebRTC channel is open, all traffic is peer-to-peer and end-to-end encrypted. |
| 99 | +### Yjs CRDTs |
| 100 | +CRDTs ensure concurrent edits merge deterministically without a central authority. |
69 | 101 |
|
70 | | -### Derived Passwords |
71 | | -To protect the LEK, the password for the WebRTC "room" is derived from the LEK using HKDF. This ensures that even if the signaling server or the WebRTC layer is compromised, the raw LEK is never exposed. |
| 102 | +### Nostr Events |
| 103 | +Bookmarks are published as kind 30053 (parameterized replaceable) events. Content is always AES-256-GCM encrypted before publishing. The `d` tag enables efficient querying and replacement. |
72 | 104 |
|
73 | 105 | ## Component Overview |
74 | 106 |
|
75 | | -- **`src/components/`**: Divided into feature domains (bookmarks, pairing, sync, ui). |
76 | | -- **`src/services/`**: Pure logic services for bookmarks, crypto, device registry, and signaling. |
77 | | -- **`src/hooks/`**: React-style hooks that bridge Yjs and search functionality into the UI. |
78 | | -- **`src/utils/`**: Shared utilities for device identification and QR processing. |
| 107 | +- **`src/components/`**: Feature domains (bookmarks, pairing, ui) |
| 108 | +- **`src/services/`**: Core logic (bookmarks, crypto, nostr-sync, nostr-crypto, key-storage) |
| 109 | +- **`src/hooks/`**: React hooks (useYjs, useNostrSync, useSearch) |
| 110 | +- **`src/utils/`**: Shared utilities |
79 | 111 |
|
80 | 112 | For detailed security implementation, see [Security](security.md). |
| 113 | +For Nostr sync details, see [Nostr Sync Architecture](nostr-sync-architecture.md). |
0 commit comments