When images are attached, a Note/Picture toggle appears letting the user publish as kind 20 (PictureEvent) instead of kind 1. Text input is disabled in picture mode. Selector only shows for image file types. Updates testing plan: Phase 1 all pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4.2 KiB
Brainstorm: Desktop DM Encrypted Media (Phase 6)
Date: 2026-03-18
Status: Ready for planning
Branch: feat/desktop-media
What We're Building
Full send + receive encrypted media support in desktop DM chat (NIP-17). Users can attach files in DMs, which get encrypted (AES-GCM) before upload to Blossom, sent as ChatMessageEncryptedFileHeaderEvent (kind 15) wrapped in GiftWrap. Received encrypted media is downloaded, decrypted, and displayed inline with a lock icon overlay.
Why This Approach
The protocol layer is complete in quartz (NIP-17, NIP-44, AESGCM, ChatMessageEncryptedFileHeaderEvent). Android has the full flow implemented. Desktop already has EncryptedMediaService.downloadAndDecrypt() and DesktopUploadOrchestrator (unencrypted only). The work is essentially wiring up the existing pieces with a desktop-native UX.
Key Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Scope | Full send + receive | Both sides needed for complete DM file sharing |
| Attach UX | Inline paperclip button | Matches desktop chat conventions; thumbnails above text input |
| Drag & drop | Yes, in addition to button | Desktop-native interaction; ComposeNoteDialog already has this pattern |
| Encryption indicator | Lock icon overlay | Small lock on corner of encrypted media in chat bubbles |
| Upload encryption | AES-GCM via AESGCM class | Matches Android's ChatFileUploader.justUploadNIP17() pattern |
| Event type | Kind 15 (ChatMessageEncryptedFileHeaderEvent) | NIP-17 standard for encrypted file metadata |
Architecture Overview
Send Flow (Desktop)
User attaches file → thumbnail preview above input
→ Click send
→ Generate AES-GCM cipher (key + nonce)
→ DesktopUploadOrchestrator.uploadEncrypted(cipher, file)
→ Encrypt file bytes with cipher
→ Upload encrypted blob to Blossom server
→ Build ChatMessageEncryptedFileHeaderEvent (kind 15)
→ URL, encryption algo/key/nonce, file metadata
→ Wrap in GiftWrap (NIP-59) for each recipient
→ Send to relays
Receive Flow (Desktop)
Receive GiftWrap → unwrap → ChatMessageEncryptedFileHeaderEvent
→ Extract URL, encryption key, nonce from tags
→ EncryptedMediaService.downloadAndDecrypt(url, key, nonce)
→ Display decrypted media inline in chat bubble
→ Lock icon overlay on media thumbnail
Existing Code to Reuse
| Component | Location | Action |
|---|---|---|
| AESGCM cipher | quartz/utils/ciphers/AESGCM.kt |
Reuse as-is |
| ChatMessageEncryptedFileHeaderEvent | quartz/nip17Dm/files/ |
Reuse as-is |
| NIP17Factory (GiftWrap) | quartz/nip17Dm/NIP17Factory.kt |
Reuse as-is |
| EncryptedMediaService | desktopApp/service/media/EncryptedMediaService.kt |
Extend for UI integration |
| DesktopUploadOrchestrator | desktopApp/service/upload/DesktopUploadOrchestrator.kt |
Add uploadEncrypted() |
| ChatPane | desktopApp/ui/chats/ChatPane.kt |
Add attach button, thumbnails, drag-drop |
| ChatMessageCompose | commons/ui/chat/ChatMessageCompose.kt |
Add encrypted media display |
| Android ChatFileUploader | amethyst/chats/privateDM/send/upload/ |
Reference pattern |
New Code Needed
| Component | Location | Purpose |
|---|---|---|
uploadEncrypted() |
DesktopUploadOrchestrator | Encrypt file with AESGCM before Blossom upload |
| DM file attach UI | ChatPane.kt | Paperclip button, thumbnail row, drag-drop zone |
| DM file send logic | ChatPane.kt or new helper | Build kind 15 event from upload result + cipher |
| Encrypted media renderer | ChatMessageCompose or new composable | Download, decrypt, display with lock overlay |
sendNip17EncryptedFile() |
DesktopIAccount.kt | Bridge to relay manager for sending |
Open Questions
None — all key decisions resolved through brainstorm dialogue.
Test Cases (from testing plan)
| # | Test | Expected |
|---|---|---|
| 6.1 | DM file attach | Attach button visible, encryption indicator shown |
| 6.2 | Send encrypted | File uploads encrypted to Blossom, kind 15 event sent |
| 6.3 | Receive encrypted | Encrypted file downloads, decrypts, displays in bubble |
| 6.4 | Wrong key | Decryption fails gracefully (no crash, error state) |