From 8fe93fa63b5e85d34ff81ca972c36757fde83567 Mon Sep 17 00:00:00 2001 From: "Evgeny @ SimpleX Chat" <259188159+evgeny-simplex@users.noreply.github.com> Date: Mon, 11 May 2026 18:03:31 +0000 Subject: [PATCH] update --- .../2026-03-15-simplex-web-widget-product.md | 302 +++++++-- plans/2026-03-15-simplex-web-widget.md | 622 ++++++++++++++++++ 2 files changed, 853 insertions(+), 71 deletions(-) create mode 100644 plans/2026-03-15-simplex-web-widget.md diff --git a/plans/2026-03-15-simplex-web-widget-product.md b/plans/2026-03-15-simplex-web-widget-product.md index 18ea14d66c..cb0b17ccfb 100644 --- a/plans/2026-03-15-simplex-web-widget-product.md +++ b/plans/2026-03-15-simplex-web-widget-product.md @@ -127,48 +127,144 @@ Signal could build a widget, but their conversations are 1:1. Customer wouldn't - Can I continue this conversation later? - Can I take this conversation with me to the app? -**Requirements:** +**Requirements (MVP):** - No app install, no account creation -- Clear privacy indicator -- "encrypted" badge or similar -- Standard chat UX -- typing indicators, read receipts -- File/image sharing for support scenarios -- Notification when response arrives (browser notification permission) -- Session persistence across page navigation on same site -- Migration path to SimpleX app via QR code -- Graceful degradation -- clear message if connection fails -- Accessibility -- screen readers, keyboard navigation +- Clear privacy indicator -- "via SimpleX Network" +- Standard chat UX -- sent/delivered checkmarks +- File sharing via upload page link +- Session persistence across tabs and sessions +- Graceful degradation -- retry on failure + +**Requirements (Post-MVP):** + +- Migration path to SimpleX app +- In-widget file upload +- Typing indicators +- Full accessibility support ## Product Flow ### Site Owner Setup -1. Create SimpleX contact address or deploy support bot +1. Create SimpleX business address or deploy support bot 2. Add script tag to site with address parameter and version hash -3. Optionally customize appearance +3. Optionally set accent color and dark mode sync 4. Done Recommended: Deploy open-source support bot that auto-accepts contact requests and routes to agents. We provide the bot. ### Visitor Experience -1. Visit site, see chat bubble -2. Click bubble, chat window opens -3. Type message, send -4. If bot: immediate connection, conversation starts -5. If manual address: widget shows "connecting..." with option to enable notification -6. Conversation proceeds in browser -7. If visitor wants to continue on app: scan QR code to migrate conversation +**Widget closed:** +- Chat bubble with shield icon in bottom-right +- Text: "Talk to us privately via SimpleX Network" -### Migration to App +**Widget opens (not connected yet):** +- Business name, logo, welcome message (from address data) +- Name field with placeholder "name or pseudonym" +- Incognito button (mask icon) to generate random name +- Message entry field +- Visitor sees name before sending -- conscious choice -Visitor can scan QR code from browser widget. This transfers: -- Conversation history -- Connection to site owner -- Encryption keys +**Send tapped:** +- First message included in connection request (single action) +- Single checkmark = sent, double checkmark = delivered +- "Keep this tab open to receive replies" instruction +- "Notify me when [Business] replies" button (browser notification) +- If SMP router down: retry button -Conversation continues in SimpleX Chat app with full durability. +**Conversation:** +- Standard chat UI +- Agent joins shown as pronounced system message +- Replies/reactions from site owner displayed (visitor can't create in MVP) +- Conversation persists across sessions and tabs +- Same conversation in all tabs on same site + +**Mobile:** +- Full screen when expanded +- Back button to return to bubble + +**Ending conversation:** +- Visitor can delete -- always notifies owner, reverts to fresh state +- Owner deletes -- visitor sees notification (if sent) or delivery fails on next send +- History preserved on visitor side until visitor deletes + + +## UX Specification + +### Widget Closed (Bubble) + +- Pointy bubble shape with shield icon, no SimpleX logo +- Text: "Talk to us privately via SimpleX Network" +- Position: bottom-right +- Unread indicator when response waiting + +### Widget Opens + +Connection happens on SEND, not on open. Widget shows: + +- Business name, logo, welcome message (from address data) +- Name field with placeholder "name or pseudonym" +- Incognito button (mask icon) fills random name from reduced dictionary +- User sees random name before sending -- transparency, conscious choice +- Message entry field + +### Send Tapped + +- First message included in connection request (single action) +- "Notify me when [Business] replies" button above entry +- "Keep this tab open to receive replies" instruction +- If SMP router down: retry button + +### Conversation + +- Single checkmark = sent, double checkmark = delivered +- No typing indicators (bot is instant, mental model is async) +- No read receipts (SimpleX doesn't support them) +- Agent joins: pronounced system message ("Alex joined the conversation") +- Replies and reactions from site owner displayed; visitor cannot create them in MVP +- File sharing: link to simplex.chat/file upload page + +### Persistence + +- Conversation persists across sessions until visitor explicitly deletes +- Same conversation across all tabs on same site +- Return visitor sees history immediately + +### Conversation End + +**Owner ends:** +- Owner "deletes contact" in SimpleX Chat +- If owner notifies: visitor sees notification +- If owner deletes quietly: visitor discovers on next send (delivery fails) +- Visitor still sees history until they delete + +**Visitor ends:** +- Always notifies owner (no quiet removal) +- Reverts to fresh state +- Can start new conversation immediately + +### Mobile + +- Full screen when expanded +- Chat bubble when closed +- Standard touch targets (48px minimum) +- Back button / X to return to bubble + +### Customization + +- Accent color matches site +- Dark mode with API to sync with site's dark mode +- Welcome message from business address data +- Position: bottom-right (configurable post-MVP) + +### Notifications (MVP) + +- "Keep this tab open to receive replies" +- Browser notification permission for in-tab alerts +- Web Push via notification router is post-MVP ## Trust Model @@ -188,63 +284,127 @@ Site owner controls the JS. Visitor trusts site owner not to exfiltrate messages The architectural guarantee: no party other than visitor and site owner can read the messages. SimpleX network, hosting providers, CDNs, ad networks -- none of them have access. +## MVP Scope + +### In MVP + +**Widget UI:** +- Chat bubble with shield icon, "Talk to us privately via SimpleX Network" +- Business name, logo, welcome message from address data +- Name field with incognito button (mask icon) +- First message in connection request +- Single/double checkmark for sent/delivered +- Agent join notifications (pronounced) +- Display replies and reactions from site owner +- "Keep this tab open" instruction +- Dark mode with API to sync with site +- Accent color customization +- Full screen on mobile + +**Persistence:** +- Same conversation across sessions and tabs +- History preserved until visitor deletes +- Visitor deletion notifies owner + +**File sharing:** +- Link to simplex.chat/file or site's upload page + +**Documentation:** +- Setup guide for JS integration +- Bot setup guide + +### Post-MVP + +- Web Push notifications (via notification router + UnifiedPush protocol) +- Migration to app (QR code transfer) +- In-widget file upload +- Visitor replies and reactions +- Typing indicators +- Full accessibility support +- Full theme customization +- Position options + +### First Adopters + +1. simplex.chat +2. Evgeny's personal site +3. Friendly community sites + + +## Resolved Questions + +### Spam/Abuse +Bot with captcha protection, same as directory groups. Not immediate concern at current network stage. + +### Multi-tab +Same conversation across all tabs. Tech design will determine storage mechanism. + +### Notifications (MVP) +"Keep this tab open" instruction. Web Push is post-MVP. + +### Branding +"via SimpleX Network" visible. No white-label option -- branding IS the value. + + +## Business Model + +The widget can be free or paid, at the discretion of each SMP router operator. + +**How it works**: Browser WebSocket connections include an `Origin` header (browser-enforced, not spoofable). The SMP server sees which website is embedding the widget. The server controls access via CORS — it returns `Access-Control-Allow-Origin` only for allowed domains. Without the correct CORS header, the browser blocks the connection. + +**What the server sees**: origin domain (which site) and usage volume (number of connections/commands). The server does NOT see who the users are, message content, or any per-user information. + +**Billing model options for router operators**: +- Free for all domains (open access) +- Free with trial period, paid after (per-domain) +- Paid from start (per-domain, usage-based) +- Allowlist only (specific domains) + +**Implementation**: server checks `Origin` header against configured domain list during WebSocket upgrade. Allowed domains get CORS headers and proceed. Unknown domains are rejected. Trial expiry, usage limits, and billing are operator concerns — the protocol layer just provides the access control mechanism. + +This enables SimpleX router operators to monetize widget hosting without compromising user privacy — billing is per-site, not per-user. + +## Privacy Model + +**What SMP router operators can observe**: +- Which sites embed the widget (Origin header) +- Usage volume per site (connection/command counts) +- Visitor IP addresses (from TCP connections) +- Connection timing and patterns + +**What they cannot observe**: +- Message content (end-to-end encrypted) +- User identities (no accounts, no cookies) +- Which queues belong to which visitors + +**Visitor IP addresses**: SMP servers can observe visitor IP addresses from WebSocket connections. Visitors concerned about IP privacy should use Tor or a VPN. + ## Open Questions -### Spam/Abuse Mitigation - -Open contact is open to abuse. Primary mitigation: bot with captcha protection, same pattern as directory groups. - -Current network state: spam happens in groups but not in direct contacts yet. This may change as network grows, but not an immediate concern. - -Additional options if needed: -- Rate limiting at widget level -- Proof-of-work before connect - ### Team Workflow - -Real support teams have multiple agents, shifts, handoffs. The bot handles routing. But: -- Are all agents using SimpleX Chat app? -- Is there a dashboard for teams? -- How do handoffs work? - -### Multi-tab / Multi-device - -Visitor opens site in two tabs, or switches from phone to desktop. What happens? -- Ephemeral keys per tab? Conversation isolated. -- Shared keys via localStorage? Tabs can conflict. -- Server-side session? Adds complexity. - -### Offline Message Queueing - -Visitor sends message, site owner offline. Options: -- Message queued at SMP router (standard SimpleX behavior) -- Widget shows "message sent, waiting for response" -- Visitor enables notification, closes tab, gets notified when response arrives +- Agents use SimpleX Chat app +- Bot handles routing and escalation +- Dashboard is out of scope (agents use app) ### Trust Verification - -Visitor sees chat bubble. How do they know this is actually SimpleX? -- Visual indicator in widget -- Link to verify on simplex.chat -- Certificate/signature verification (complex in browser) +- "via SimpleX Network" text provides some verification +- Link to simplex.chat for more info +- Full verification is complex in browser -- acceptable tradeoff ## Success Metrics -**Site Owner:** +**Measurable without tracking:** +- Number of sites embedding widget (observable) - Integration time < 5 minutes - Zero support requests for setup -- Uptime > 99.9% -**Visitor:** -- Time to first message < 10 seconds -- Conversion to app install (tracked via QR scan) -- Return visitor rate +**Not measurable (would require tracking that contradicts value proposition):** +- Message counts +- App conversion rates +- Visitor behavior -**Ecosystem:** -- Number of sites using widget -- Messages per day through widget -- New SimpleX app installs attributed to widget +We accept limited visibility as a feature, not a bug. ## Competitive Landscape @@ -357,10 +517,10 @@ The customers we refuse are as important as the customers we serve. Trying to se 1. **Browser limitations** -- No TLS certificate pinning. Trust browser's CA system. Acceptable for threat model. -2. **Ephemeral by default** -- Visitor closes browser, conversation gone (unless migrated). Feature or bug depends on use case. +2. **Persistence complexity** -- Conversation must persist across sessions and sync across tabs. Storage mechanism TBD in tech design. 3. **JS supply chain** -- Site owner embeds our JS. Mitigated by including hash in script tag (Subresource Integrity). Browser refuses to execute if hash doesn't match. Self-host option also available. -4. **Adoption chicken-egg** -- Visitors don't know what SimpleX is. Widget must explain value without friction. +4. **Adoption chicken-egg** -- Visitors don't know what SimpleX is. "via SimpleX Network" text helps, but brand recognition takes time. -5. **Support complexity** -- We're building infrastructure for support teams. They have expectations from Intercom/Zendesk. We can't match feature parity, shouldn't try. +5. **Background tab limitations** -- Browsers throttle/suspend background tabs. MVP uses "keep tab open" instruction. Post-MVP may add Web Push via notification router. diff --git a/plans/2026-03-15-simplex-web-widget.md b/plans/2026-03-15-simplex-web-widget.md new file mode 100644 index 0000000000..1031d58c06 --- /dev/null +++ b/plans/2026-03-15-simplex-web-widget.md @@ -0,0 +1,622 @@ +# SimpleX Web Widget: Master Plan + +Revision 1, 2026-03-15 + +**Status**: Planning complete. Spike next. + +**Related documents**: +- [Product Plan](./2026-03-15-simplex-web-widget-product.md) -- Users, UX, scope +- [Spike Plan](./2026-03-15-simplex-web-widget/2026-03-15-simplex-web-widget-spike.md) -- LGET proof of concept + +## Overview + +This document defines what needs to be built for the web widget at each layer. It is a master plan -- high-level structure, not exhaustive function mapping. + +The widget runs entirely in browser. It connects to SMP routers via WebSocket, implements the subset of agent functionality needed for messaging, and provides chat-level features (connection to any address type, message display, delivery status). + +**Layers:** + +``` +┌─────────────────────────────────────────┐ +│ Widget UI (React/Preact) │ ← User interaction +├─────────────────────────────────────────┤ +│ Chat Layer (TypeScript) │ ← Address types, message handling +├─────────────────────────────────────────┤ +│ Agent Layer (TypeScript) │ ← Connection lifecycle, encryption +├─────────────────────────────────────────┤ +│ SMP Client Layer (TypeScript) │ ← WebSocket transport, protocol +└─────────────────────────────────────────┘ + │ + ▼ WebSocket (TLS) + ┌───────────────┐ + │ SMP Router │ + └───────────────┘ +``` + + +## Phase 0: Spike + +**Goal**: Prove the whole thing works at minimum cost. No production code -- throwaway. + +### What the Spike Does + +1. Parse contact/business address URI +2. Open WebSocket to SMP router (direct, no proxy) +3. Send LGET command to fetch link data +4. Decrypt profile data +5. Display in browser console + +### What This Proves + +- WebSocket transport to SMP router works from browser +- Protocol encoding is correct (LGET parses, response decodes) +- Crypto is compatible (decryption works) +- The whole thing is buildable + +### What Spike Does NOT Need + +- Sending connection request (more complex, doesn't add to proof) +- SKEY/SEND commands +- Proxy/private routing +- Double ratchet +- Persistence +- Any UI + +### Deliverable + +- Browser console demo: paste address URI, see profile data +- Throwaway TypeScript code +- Notes on what was tricky (encoding, crypto, WebSocket framing) + + +## Phase 1: Router Infrastructure + +**Prerequisite for spike.** SMP routers need WebSocket upgrade support for web clients. + +### Current State + +- `Transport/WebSockets.hs` exists but runs in exclusive mode +- Router is EITHER WebSocket OR normal HTTP -- not both +- Info page served on HTTP, no upgrade path for browser JS + +### Required Changes + +1. **WebSocket upgrade on same port** + - HTTP request with `Upgrade: websocket` header triggers upgrade + - Normal browser navigation still shows info page + - Same TLS connection, just protocol switch + +2. **CORS headers** + - `Access-Control-Allow-Origin` for widget domains + - Allow cross-origin WebSocket connections + +3. **No service certificate changes** + - Widget uses regular client auth, not service certs + - No changes needed for MVP + +### Implementation Location + +- `Simplex.Messaging.Server` -- HTTP handler modification +- `Simplex.Messaging.Transport.WebSockets` -- already has WebSocket logic +- Need to bridge: HTTP request → check upgrade → WebSocket transport + +### Deliverable + +- SMP routers accept WebSocket upgrade from browsers +- Info page still works for direct navigation +- CORS configured for widget embedding + + +## Phase 2: SMP Client Layer (TypeScript) + +Minimal protocol client that can send/receive SMP commands over WebSocket. + +### Transport + +- WebSocket connection with TLS (browser handles certificate validation) +- Block framing: 16384 byte blocks, same as native client +- Reconnection with exponential backoff + +### Protocol Subset + +Commands needed for widget: + +| Command | Purpose | +|---------|---------| +| NEW | Create receive queue (for visitor's side of connection) | +| KEY | Register sender key on receive queue | +| SKEY | Authenticate as sender on queue | +| SUB | Subscribe to queue for messages | +| ACK | Acknowledge message receipt | +| SEND | Send message to queue | +| OFF | Disable queue (when visitor deletes conversation) | +| DEL | Delete queue | + +Proxied messaging (private routing): + +| Command | Purpose | +|---------|---------| +| PRXY | Request proxy session to destination router | +| PFWD | Forward command through proxy | + +### Correlation + +- Generate correlation IDs for commands +- Match responses to pending requests +- Timeout handling for unresponsive routers + +### Deliverable + +- `SMPClient` class: connect, disconnect, send command, receive response/event +- Connection pooling (one connection per router) +- Automatic reconnection + + +## Phase 3: Agent Layer (TypeScript) + +Connection lifecycle and encryption. This is the core complexity. + +### Key Management + +- Generate X25519 key pairs (DH for queue auth) +- Generate X448 key pairs (PQ-resistant ratchet init) +- Store keys in IndexedDB (encrypted with derived key from random secret in localStorage) + +### Connection Establishment + +Support joining via invitation URI (the common case for widget): + +1. Parse invitation URI (extract queue address, DH keys) +2. Create local receive queue (NEW) +3. Initialize sending ratchet (X3DH key agreement) +4. Send confirmation to inviter's queue (SKEY + SEND) +5. Wait for reply with inviter's queue info +6. Complete ratchet initialization +7. Exchange HELLO messages +8. Connection ready + +Also support creating invitation (for future use cases): + +1. Create receive queue (NEW) +2. Generate invitation URI with queue address + DH keys +3. Wait for joiner's confirmation +4. Complete connection handshake + +### Double Ratchet + +- Implement Signal double ratchet for message encryption +- Header encryption (prevents metadata leakage) +- Ratchet state persistence in IndexedDB +- Key derivation using HKDF + +### Message Processing + +Receive path: +1. Decrypt with double ratchet +2. Verify message integrity (sequence + hash chain) +3. Store in local database +4. Emit event to chat layer +5. ACK to router (can be batched) + +Send path: +1. Advance ratchet, store pending message with encryption key +2. Encrypt body with stored key +3. Encode agent message envelope +4. SEND to router +5. On OK, mark as sent; on delivery receipt, mark as delivered + +### Persistence + +- IndexedDB for: keys, ratchet state, messages, connection state +- Cross-tab coordination via BroadcastChannel or SharedWorker +- Single "active" tab owns WebSocket connections, others receive via broadcast + +### Deliverable + +- `AgentClient` class: createConnection, joinConnection, sendMessage, ackMessage +- Event emitter for received messages and status updates +- Persistence layer with cross-tab sync + + +## Phase 4: Chat Layer (TypeScript) + +Address type handling and message semantics. + +### Address Types + +Widget must handle any SimpleX address type: + +1. **Contact address** -- 1:1 conversation with site owner +2. **Group link** -- Join group, see all members +3. **Business address** -- Hybrid: looks like 1:1 to visitor, but is group internally + +All three use same underlying protocol. Difference is in: +- How connection is established (contact request vs group join) +- What metadata is shown (single owner vs multiple agents) +- Message attribution (who sent what) + +### Connection Flow by Type + +**Contact address:** +- Parse contact URI +- Join connection with chosen display name +- First message included in connection request +- Owner accepts, connection established + +**Group link:** +- Parse group link URI +- Join group with chosen display name +- Receive group info, member list +- Can see and message all members + +**Business address:** +- Parse business address URI (same format as contact) +- Join with display name +- Internally creates business chat (group with special properties) +- Visitor sees business name, may see individual agent names on messages +- Agents can join/leave without visitor needing to know + +### Message Types + +Display in widget: + +| Type | Handling | +|------|----------| +| Text | Display as message bubble | +| Agent join | System message: "Alex joined the conversation" | +| Delivery receipt | Update checkmarks (single → double) | +| File reference | Link to upload page | +| Reaction | Display on referenced message | +| Reply | Display with quoted content | + +Create in widget (MVP): + +| Type | Handling | +|------|----------| +| Text | Primary message type | +| Delete notification | When visitor deletes conversation | + +Post-MVP: + +| Type | Handling | +|------|----------| +| Reaction | Visitor can react to messages | +| Reply | Visitor can quote messages | +| File | Direct upload | + +### State Management + +- Current connection state (disconnected, connecting, connected) +- Message list with delivery status +- Unread count for bubble indicator +- Business/contact metadata for display + +### Deliverable + +- `ChatClient` class: connect to address, send message, receive events +- Address parsing for all three types +- Message rendering helpers + + +## Phase 5: Widget UI + +React or Preact component library. Smallest reasonable bundle. + +### Components + +- `ChatBubble` -- Collapsed state, unread indicator +- `ChatWindow` -- Expanded state, message list, input +- `ConnectionScreen` -- Name input, incognito button, first message +- `MessageList` -- Scrolling message display +- `MessageInput` -- Text entry, send button +- `SystemMessage` -- Agent joins, connection status + +### Customization API + +```typescript +interface WidgetConfig { + address: string; // SimpleX address URI + accentColor?: string; // Hex color for buttons, links + darkMode?: boolean | 'auto'; // Sync with site or explicit + position?: 'bottom-right'; // MVP: only bottom-right + welcomeMessage?: string; // Override from address data +} +``` + +### Embedding + +```html + +``` + +Script tag creates widget automatically. Integrity hash ensures version pinning. + +### Self-hosting + +Site owners can host `widget.js` themselves for maximum trust. Same API, different src. + +### Deliverable + +- Minimal component library +- CSS-in-JS or minimal CSS (no external dependencies) +- Bundle size target: < 100KB gzipped (including crypto) + + +## Design Principles + +### Mirror Haskell Structure + +TypeScript code should model Haskell function names and module structure: + +- `Simplex.Messaging.Protocol` → `protocol.ts` with same type/function names +- `Simplex.Messaging.Parsers` → `parser.ts` +- `serializeSMPCommand` in Haskell → `serializeSMPCommand` in TypeScript +- `smpCommandP` parser → `smpCommandP` + +This enables: +- Easy cross-reference between codebases +- Sync as protocol evolves +- Code review by people who know Haskell side + + +## Prior Art: xftp-web + +`../simplexmq-2/xftp-web/` is the production reference for browser TypeScript. + +### Direct Reuse + +**Crypto** (`src/crypto/`): +- `secretbox.ts` -- XSalsa20-Poly1305 streaming, matches Haskell Crypto.hs +- `digest.ts` -- SHA-256/512 via libsodium +- `keys.ts` -- Ed25519/X25519, DER encoding, key hash +- `identity.ts` -- X.509 certificate verification + +**Encoding** (`src/protocol/encoding.ts`): +- `Decoder` class -- sequential binary parser +- Integer encoding (Word16, Word32, Int64 big-endian) +- ByteString, Large, Tail patterns +- Comments reference Haskell line numbers + +### Libraries Used + +```json +{ + "libsodium-wrappers-sumo": "^0.7.13", + "@noble/curves": "^1.4.0" +} +``` + +libsodium for most crypto, @noble/curves for Ed448 (not in libsodium). + + +## Prior Art: simplexmq-js + +`../simplexmq-js/` contains a working SMP client from ~2021. Protocol has evolved (v0.4 → v19), but patterns transfer. + +### Reuse (patterns, not code) + +**Parser** (`parser.ts`): +- Combinator-style scanner that avoids string splits +- `&&` chaining models Haskell grammar-based parsers +- Returns `undefined` on failure, enabling clean call sites: +```typescript +MSG: (p) => { + let msgId, msg: Uint8Array | undefined + let ts: Date | undefined + return ( + p.space() && (msgId = p.base64()) && p.space() && + (ts = p.date()) && p.space() && (msg = messageP(p)) && + cMSG(msgId, ts, msg) + ) +} +``` + +**Protocol types** (`protocol.ts`): +- One type per command, mirrors Haskell ADT +- Constructors: `cNEW`, `cMSG`, `cLGET`, etc. +- Binary tag tables for serialization + +**Buffer utilities** (`buffer.ts`): +- Base64 encode/decode +- Binary concatenation +- Integer encoding + +**Async bounded queue** (`queue.ts`): +- `ABQueue` with semaphore-based backpressure +- Async iterator protocol -- `for await (const msg of transport)` +- Clean close semantics with sentinel +- This pattern is solid, likely reusable + +### Review Before Reuse + +**WebSocket transport** (`transport.ts`): +- `WSTransport` is ~30 lines, simple enough +- Connect, wire handlers to ABQueue, wait for open +- May be fine as-is, review during spike +- Custom RSA handshake (`SMPTransport`) no longer applies -- now TLS +- No socket.io needed -- plain WebSocket is universal + +### Do Not Reuse + +**Crypto** (`crypto.ts`): +- Was RSA-OAEP + AES-GCM +- Now X25519 + XChaCha20-Poly1305 +- Use `@noble/*` libraries instead + +### Pre-implementation Task + +Scan `simplexmq-js` for utilities worth adapting: +- `Parser` class +- `ABQueue` (async bounded queue) +- Buffer helpers +- Type patterns + +Document what transfers vs what needs rewrite. + + +## Haskell Modules to Reference + +TypeScript should mirror these modules: + +### Protocol Layer (simplexmq) +| Haskell | TypeScript | Purpose | +|---------|------------|---------| +| `Simplex.Messaging.Protocol` | `protocol.ts` | Commands, responses, encoding | +| `Simplex.Messaging.Transport` | `transport.ts` | Handshake, block framing | +| `Simplex.Messaging.Encoding` | `encoding.ts` | smpEncode/smpP patterns | +| `Simplex.Messaging.Parsers` | `parser.ts` | Parser combinators | +| `Simplex.Messaging.Crypto` | `crypto.ts` | Encryption primitives | +| `Simplex.Messaging.Crypto.ShortLink` | `shortLink.ts` | KDF, link encryption | + +### Agent Layer (simplexmq) +| Haskell | TypeScript | Purpose | +|---------|------------|---------| +| `Simplex.Messaging.Agent.Protocol` | `agentProtocol.ts` | Connection types, link data | +| `Simplex.Messaging.Agent.Client` | `agentClient.ts` | Connection management | +| `Simplex.Messaging.Crypto.Ratchet` | `ratchet.ts` | Double ratchet | + +### Chat Layer (simplex-chat) +| Haskell | TypeScript | Purpose | +|---------|------------|---------| +| `Simplex.Chat.Protocol` | `chatProtocol.ts` | Message types | +| `Simplex.Chat.Types` | `chatTypes.ts` | Profile, contacts | + + +## Dependencies and Build + +### Crypto Libraries + +- `@noble/curves` -- X25519, X448, Ed25519 (audited, pure JS) +- `@noble/ciphers` -- ChaCha20-Poly1305, XSalsa20 (audited, pure JS) +- `@noble/hashes` -- SHA-256, SHA-512, HKDF (audited, pure JS) + +These are the same libraries used by many crypto projects, well-audited, no WASM. + +### Encoding + +- CBOR for message encoding (same as native client) +- Custom binary encoding for SMP protocol frames + +### Build + +- esbuild or Vite for bundling +- Tree-shaking to minimize size +- Separate chunks for core vs UI (allow headless use) + + +## Testing Strategy + +### Unit Tests + +- Protocol encoding/decoding +- Crypto operations (encrypt/decrypt round-trip) +- Ratchet state transitions +- Address parsing + +### Integration Tests + +- Connect to test router +- Full connection establishment +- Message send/receive +- Reconnection handling + +### E2E Tests + +- Widget embedding in test page +- User flow: open, enter name, send message +- Cross-tab synchronization +- Persistence across page reload + + +## Milestones + +### M0: Spike -- Prove Buildability + +**Objective**: Fetch and display business profile from address URI. Minimum code to prove e2e works. + +**Scope**: +- Parse address URI +- WebSocket to SMP router (direct, no proxy) +- LGET command +- Decrypt profile data +- Display in console + +**Success criteria**: Paste address URI in browser console, see profile data displayed. + +--- + +### M1: Protocol Foundation +- SMP client with WebSocket transport +- Full command set (NEW, KEY, SKEY, SUB, ACK, SEND, OFF, DEL) +- Connection pooling, reconnection with backoff +- Correlation ID handling + +### M2: Connection Establishment +- Key generation and storage +- X3DH key agreement +- Join connection via invitation +- HELLO exchange +- Connection state machine + +### M3: Encrypted Messaging +- Double ratchet implementation +- Send and receive messages +- Delivery receipts + +### M4: Persistence +- IndexedDB storage +- Cross-tab coordination +- Session continuity + +### M5: Chat Layer +- All address types +- Message type handling +- Agent join notifications + +### M6: Widget UI +- Component library +- Embedding API +- Customization + +### M7: Polish +- Error handling +- Reconnection UX +- Performance optimization +- Documentation + + +## Open Questions + +### Encoding Approach +- XFTP web used direct Haskell-to-TypeScript port +- You mentioned showing a different encoding approach -- waiting for that example + +### Bundle Size +- 100KB target may be aggressive with full crypto +- Could split: tiny loader + async load main bundle +- Measure actual size after M1 + +### Browser Support +- Modern browsers only (ES2020+) +- No IE11, no legacy Edge +- Safari WebSocket behavior needs testing + +### Proxy Selection +- How does widget choose which proxy router to use for private routing? +- Hardcoded list? Dynamic discovery? Site owner config? + + +## Not In Scope + +- Web Push notifications (post-MVP, uses notification router) +- Migration to app (post-MVP, QR code transfer) +- File upload (post-MVP, in-widget upload) +- Typing indicators (not supported in protocol yet) +- Full accessibility (deferred) +- White-label (counter to value proposition)