|
| 1 | +# Graphiti Architecture |
| 2 | + |
| 3 | +This document describes the system architecture of the Graphiti Chrome Extension. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +Graphiti is a Chrome Extension built on Manifest V3 that enables users to interact with web pages through drawing, annotations, bookmarks, and tags, all synchronized via the Pubky decentralized network. |
| 8 | + |
| 9 | +## High-Level Architecture |
| 10 | + |
| 11 | +``` |
| 12 | +┌─────────────────────────────────────────────────────────────────────┐ |
| 13 | +│ Chrome Browser │ |
| 14 | +├─────────────────────────────────────────────────────────────────────┤ |
| 15 | +│ │ |
| 16 | +│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ |
| 17 | +│ │ Popup │ │ Side Panel │ │ Profile Page │ │ |
| 18 | +│ │ │ │ │ │ │ │ |
| 19 | +│ │ - Auth UI │ │ - Feed viewer │ │ - Profile │ │ |
| 20 | +│ │ - Quick actions │ │ - Post list │ │ rendering │ │ |
| 21 | +│ │ - Profile edit │ │ - Annotations │ │ │ │ |
| 22 | +│ └────────┬─────────┘ └────────┬─────────┘ └──────────────────┘ │ |
| 23 | +│ │ │ │ |
| 24 | +│ └─────────┬───────────┘ │ |
| 25 | +│ ▼ │ |
| 26 | +│ ┌──────────────────────────────────────────────────────────────┐ │ |
| 27 | +│ │ Background Script │ │ |
| 28 | +│ │ (Service Worker) │ │ |
| 29 | +│ │ │ │ |
| 30 | +│ │ - Message routing │ │ |
| 31 | +│ │ - Keyboard shortcuts │ │ |
| 32 | +│ │ - Side panel control │ │ |
| 33 | +│ │ - Storage coordination │ │ |
| 34 | +│ └────────────────────────────┬─────────────────────────────────┘ │ |
| 35 | +│ │ │ |
| 36 | +│ ┌───────────────────┼───────────────────┐ │ |
| 37 | +│ ▼ ▼ ▼ │ |
| 38 | +│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │ |
| 39 | +│ │ Content │ │ Content │ │ Content │ │ |
| 40 | +│ │ Script │ │ Script │ │ Script │ │ |
| 41 | +│ │ (Tab 1) │ │ (Tab 2) │ │ (Tab N) │ │ |
| 42 | +│ │ │ │ │ │ │ │ |
| 43 | +│ │ - Drawing │ │ - Drawing │ │ - Drawing │ │ |
| 44 | +│ │ - Annotations │ │ - Annotations │ │ - Annotations │ │ |
| 45 | +│ │ - URL links │ │ - URL links │ │ - URL links │ │ |
| 46 | +│ └────────────────┘ └────────────────┘ └────────────────┘ │ |
| 47 | +│ │ |
| 48 | +└─────────────────────────────────────────────────────────────────────┘ |
| 49 | + │ |
| 50 | + │ HTTPS |
| 51 | + ▼ |
| 52 | + ┌───────────────────────────────────────────────┐ |
| 53 | + │ External Services │ |
| 54 | + │ │ |
| 55 | + │ ┌─────────────────┐ ┌─────────────────┐ │ |
| 56 | + │ │ Nexus API │ │ Pubky │ │ |
| 57 | + │ │ │ │ Homeserver │ │ |
| 58 | + │ │ - Post search │ │ │ │ |
| 59 | + │ │ - User lookup │ │ - Data storage │ │ |
| 60 | + │ │ - Social graph │ │ - Auth │ │ |
| 61 | + │ └─────────────────┘ └─────────────────┘ │ |
| 62 | + │ │ |
| 63 | + └───────────────────────────────────────────────┘ |
| 64 | +``` |
| 65 | + |
| 66 | +## Component Details |
| 67 | + |
| 68 | +### Background Script (Service Worker) |
| 69 | + |
| 70 | +**Location:** `src/background/background.ts` |
| 71 | + |
| 72 | +The background script is the central coordinator: |
| 73 | + |
| 74 | +- Handles Chrome extension lifecycle events |
| 75 | +- Routes messages between popup, content scripts, and sidepanel |
| 76 | +- Manages keyboard shortcuts (Alt+D for drawing, etc.) |
| 77 | +- Controls sidepanel opening/closing |
| 78 | +- Cannot access `window` or DOM (service worker limitation) |
| 79 | + |
| 80 | +### Content Scripts |
| 81 | + |
| 82 | +**Location:** `src/content/content.ts` |
| 83 | + |
| 84 | +Injected into every web page: |
| 85 | + |
| 86 | +- **DrawingManager**: Canvas overlay for drawing |
| 87 | +- **AnnotationManager**: Text selection and highlighting |
| 88 | +- **URL Linkifier**: Converts `pubky://` URLs to clickable buttons |
| 89 | + |
| 90 | +### Popup |
| 91 | + |
| 92 | +**Location:** `src/popup/` |
| 93 | + |
| 94 | +Extension popup (400x500px): |
| 95 | + |
| 96 | +- **AuthView**: QR code authentication |
| 97 | +- **MainView**: Logged-in interface with quick actions |
| 98 | +- **DebugPanel**: Log viewer |
| 99 | +- **ProfileEditor**: Profile editing form |
| 100 | + |
| 101 | +### Side Panel |
| 102 | + |
| 103 | +**Location:** `src/sidepanel/` |
| 104 | + |
| 105 | +Feed viewer: |
| 106 | + |
| 107 | +- **PostCard**: Displays posts about current URL |
| 108 | +- **AnnotationCard**: Displays annotations |
| 109 | +- **EmptyState**: Empty feed message |
| 110 | + |
| 111 | +### Utilities |
| 112 | + |
| 113 | +**Location:** `src/utils/` |
| 114 | + |
| 115 | +Shared modules: |
| 116 | + |
| 117 | +| Module | Responsibility | |
| 118 | +|--------|---------------| |
| 119 | +| `auth-sdk.ts` | Authentication via official Pubky SDK | |
| 120 | +| `crypto.ts` | Hashing, encoding, tokens | |
| 121 | +| `storage.ts` | Chrome storage wrapper | |
| 122 | +| `pubky-api-sdk.ts` | Homeserver operations | |
| 123 | +| `nexus-client.ts` | Nexus API queries | |
| 124 | +| `logger.ts` | Debug logging | |
| 125 | + |
| 126 | +## Data Flow |
| 127 | + |
| 128 | +### Authentication Flow |
| 129 | + |
| 130 | +Authentication uses the official `@synonymdev/pubky` SDK for secure, decentralized identity. |
| 131 | + |
| 132 | +**Homeserver Resolution:** |
| 133 | +The homeserver URL is derived directly from the user's public key using the `pubky://` protocol: |
| 134 | +``` |
| 135 | +homeserver = 'pubky://' + publicKey.z32() |
| 136 | +``` |
| 137 | +This ensures proper routing through the Pubky DHT without hardcoded server URLs. |
| 138 | + |
| 139 | +**Auth Flow:** |
| 140 | +``` |
| 141 | +1. User clicks "Sign In" |
| 142 | +2. Popup creates auth request via Pubky SDK (authRequest) |
| 143 | +3. SDK generates pubkyauth:// URL with relay and capabilities |
| 144 | +4. QR code displayed for scanning |
| 145 | +5. User scans with Pubky Ring mobile app |
| 146 | +6. SDK awaits response via authRequest.response() |
| 147 | +7. On approval, SDK returns authenticated PublicKey |
| 148 | +8. Session created with homeserver = 'pubky://' + publicKey.z32() |
| 149 | +9. Session stored in chrome.storage.local |
| 150 | +``` |
| 151 | + |
| 152 | +### Bookmark Flow |
| 153 | + |
| 154 | +``` |
| 155 | +1. User clicks bookmark button |
| 156 | +2. Popup sends CREATE_BOOKMARK to background |
| 157 | +3. Background/Popup creates Link Post on homeserver |
| 158 | +4. Background/Popup creates Bookmark referencing post |
| 159 | +5. Both indexed by Nexus |
| 160 | +6. Local storage updated |
| 161 | +``` |
| 162 | + |
| 163 | +### Drawing Flow |
| 164 | + |
| 165 | +``` |
| 166 | +1. User presses Alt+D |
| 167 | +2. Background sends TOGGLE_DRAWING to content script |
| 168 | +3. Content script shows canvas overlay |
| 169 | +4. User draws |
| 170 | +5. User saves (Save & Exit or Alt+D) |
| 171 | +6. Content script sends drawing data to background |
| 172 | +7. Background saves to chrome.storage.local |
| 173 | +8. Optional: Sync to homeserver |
| 174 | +``` |
| 175 | + |
| 176 | +### Feed Loading Flow |
| 177 | + |
| 178 | +``` |
| 179 | +1. Sidepanel opens |
| 180 | +2. Gets current tab URL |
| 181 | +3. Generates URL hash tag |
| 182 | +4. Queries Nexus API for posts with tag |
| 183 | +5. Renders PostCard for each result |
| 184 | +6. Also fetches annotations |
| 185 | +``` |
| 186 | + |
| 187 | +## Storage Architecture |
| 188 | + |
| 189 | +### Local Storage (chrome.storage.local) |
| 190 | + |
| 191 | +| Key | Data Type | Purpose | |
| 192 | +|-----|-----------|---------| |
| 193 | +| `session` | Session | Current auth session | |
| 194 | +| `bookmarks` | StoredBookmark[] | Local bookmarks | |
| 195 | +| `tags` | StoredTag[] | Local tags | |
| 196 | +| `profile` | ProfileData | Local profile | |
| 197 | +| `pubky_drawings` | {[url]: Drawing} | Drawings by URL | |
| 198 | +| `pubky_annotations` | Annotation[] | Local annotations | |
| 199 | +| `debugLogs` | LogEntry[] | Debug logs | |
| 200 | + |
| 201 | +### Homeserver Storage (Pubky) |
| 202 | + |
| 203 | +| Path | Data Type | Purpose | |
| 204 | +|------|-----------|---------| |
| 205 | +| `/pub/pubky.app/profile.json` | ProfileData | User profile | |
| 206 | +| `/pub/pubky.app/posts/{id}` | Post | Social posts | |
| 207 | +| `/pub/pubky.app/bookmarks/{id}` | Bookmark | Bookmarks | |
| 208 | +| `/pub/pubky.app/tags/{id}` | Tag | Tags | |
| 209 | +| `/pub/graphiti.dev/drawings/{hash}` | Drawing | Drawings | |
| 210 | + |
| 211 | +## Message Protocol |
| 212 | + |
| 213 | +Communication between components uses Chrome messaging: |
| 214 | + |
| 215 | +```typescript |
| 216 | +// Send message |
| 217 | +chrome.runtime.sendMessage({ |
| 218 | + type: 'MESSAGE_TYPE', |
| 219 | + data: { ... } |
| 220 | +}); |
| 221 | + |
| 222 | +// Handle in background |
| 223 | +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { |
| 224 | + if (message.type === 'MESSAGE_TYPE') { |
| 225 | + // Handle |
| 226 | + sendResponse({ success: true }); |
| 227 | + } |
| 228 | + return true; // Keep channel open for async |
| 229 | +}); |
| 230 | +``` |
| 231 | + |
| 232 | +### Message Types |
| 233 | + |
| 234 | +| Type | Direction | Purpose | |
| 235 | +|------|-----------|---------| |
| 236 | +| `OPEN_SIDE_PANEL` | Popup → Background | Open sidepanel | |
| 237 | +| `TOGGLE_DRAWING_MODE` | Background → Content | Toggle drawing | |
| 238 | +| `SAVE_DRAWING` | Content → Background | Save drawing | |
| 239 | +| `GET_DRAWING` | Content → Background | Load drawing | |
| 240 | +| `CREATE_ANNOTATION` | Content → Background | Create annotation | |
| 241 | +| `GET_ANNOTATIONS` | Background → Content | Load annotations | |
| 242 | +| `HIGHLIGHT_ANNOTATION` | Sidepanel → Content | Highlight text | |
| 243 | + |
| 244 | +## Build System |
| 245 | + |
| 246 | +Built with Vite: |
| 247 | + |
| 248 | +``` |
| 249 | +src/ → dist/ |
| 250 | +├── background/ → ├── background.js |
| 251 | +├── content/ → ├── content.js |
| 252 | +├── popup/ → ├── popup.html |
| 253 | +│ └── main.tsx → └── assets/popup-*.js |
| 254 | +├── sidepanel/ → ├── sidepanel.html |
| 255 | +│ └── main.tsx → └── assets/sidepanel-*.js |
| 256 | +└── utils/ → └── assets/*.js (shared chunks) |
| 257 | +``` |
| 258 | + |
| 259 | +## Security Considerations |
| 260 | + |
| 261 | +1. **Auth tokens** encrypted with client secret |
| 262 | +2. **URLs hashed** for privacy in tags |
| 263 | +3. **Service worker isolation** prevents DOM access |
| 264 | +4. **Content Security Policy** in manifest |
| 265 | +5. **Local-first storage** - data stays local unless synced |
| 266 | + |
| 267 | +## See Also |
| 268 | + |
| 269 | +- [FEATURES.md](../FEATURES.md) - Feature documentation |
| 270 | +- [Testing Documentation](TESTING.md) |
| 271 | +- [UTF-16 Encoding](UTF16_HASH_ENCODING.md) |
| 272 | + |
0 commit comments