Repository navigation
Conversation
Images sent in private chats were uploaded to Blossom as plaintext (with EXIF), and only the URL was protected by the gift-wrapped kind 14 rumor. Images are now encrypted on the device before upload and sent as NIP-17 kind 15 file messages: - Strip JPEG EXIF/XMP/IPTC/comment segments and PNG text/EXIF/time chunks without re-encoding (JPEG orientation is kept). - Encrypt with AES-256-GCM via WebCrypto using a fresh random 32-byte key and 12-byte nonce per attachment. - Upload only the ciphertext as application/octet-stream; retries reuse the same ciphertext and authorization, and the returned hash must match. - Send file-type, encryption-algorithm, decryption-key, decryption-nonce (lowercase hex), x (ciphertext SHA-256), ox and size inside the kind 15 rumor, through the existing seal + gift-wrap, self-copy and group epoch paths. The key and nonce never reach the media server. - On receive, accept kind 15 only with valid tags and an HTTPS URL, verify x before decrypting, treat GCM failures as hard errors, and render only allowlisted raster types from a decrypted, reference-counted blob: URL. 12- and 16-byte nonces are accepted for interoperability. - Keep kind 15 through relay retry/outbox replay (NDK rewrites the rumor kind to rumorKind), deletions, reactions, reply previews and forwarding (by reference, without re-uploading), and Android notifications. - Redact decryption material in developer traces and the event JSON view. Legacy kind 14 + imeta media keeps working. Video and audio still use the plaintext upload path; the plaintext uploader now refuses images.
Cleanup of the kind 15 encrypted image implementation. The NIP-17 kind 15 protocol, the AES-256-GCM scheme, and the send/receive/retry/replay/forward paths are unchanged. - Upload a single PUT with no automatic retries, so a rejected or CORS-masked upload (e.g. a 415 without CORS headers) is not sent three times; explain opaque network failures instead of "Failed to fetch". - Encrypt the original image bytes directly; metadata stripping is removed from this change (metadata is encrypted and only visible to recipients). - Load encrypted images lazily with an IntersectionObserver so long chats do not download and decrypt every image up front. - Refuse files whose leading bytes identify them as images on the plaintext video/audio upload path. - Keep NIP-17 size optional on received kind 15 instead of inventing one. - Trim the crypto and media service APIs to what production uses and reuse sha256Hex for the legacy upload hash. - Move the new upload notice to an en-US-only key and revert the locale churn; correct NIPS_USED.md.
Encrypted images no longer share the regular Blossom setting. They upload to a separate private-media server that must store ciphertext byte for byte, defaulting to https://blossom.ditto.pub. Video and audio keep using the regular server unchanged. - Add privateMediaBlossomServerUrl to private preferences, with its own getter/setter and a settings card. - Add a "Test server" probe that uploads random bytes, downloads them, compares hashes, and best-effort deletes them. - Split encryption from upload so a retry reuses the same ciphertext, key and nonce. - On upload failure, offer Retry, Change server and Cancel. There is no plaintext fallback.
Extend the encrypted private-media pipeline from images to MP4/WebM video and MP3/MP4/AAC/Ogg/WebM/WAV/FLAC audio using the existing whole-file AES-GCM scheme and kind 15 metadata. - Add centralized encrypted-media MIME and size handling. - Route private image, video and audio through the dedicated private-media Blossom server with no plaintext fallback. - Enforce bounded ciphertext downloads and per-media size limits. - Add click-to-decrypt video/audio playback with safe object URL cleanup. - Preserve ciphertext, key and nonce across upload retries and server changes. - Persist a changed private-media server only after the kind-15 send succeeds.
- Fix encrypted audio (and video) players collapsing to 0px wide: the player's width:100% resolved against a shrink-wrapped container, hiding the native controls. The media box now has a definite width. - Render encrypted video/audio as media attachments: a play placeholder until loaded, then the native player. Media from trusted senders (including our own) decrypts automatically once near the viewport; others load on tap, so long conversations are not downloaded eagerly. - Add "Don't show this again" to the private media notice, stored locally. It only skips the informational notice; uploads still go through the same encrypted flow with the same errors, retry and change-server UI. - Cover audio/video upload, decryption, playback, lazy loading, object URL release, the dismissed notice and retry with identical ciphertext in e2e.
Author
|
Superseded by #82, which ports the encrypted private media work onto the current brutal branch and its new architecture. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds end-to-end encrypted media attachments to private Anagram conversations.
Private images, videos and audio are encrypted locally with AES-256-GCM before upload. Blossom only receives ciphertext, while the information required to decrypt the attachment is carried inside the private Nostr message using kind 15.
What changed
application/octet-streamNIPS_USED.mdwith the encrypted-media behaviorThere is no automatic or plaintext fallback if an encrypted upload fails.
Compatibility
Older Anagram clients that only understand kind 14 attachments will not display the new kind 15 encrypted media messages.
Validation
quality:allpassinggit diff --checkcleanapplication/octet-streamDocker-based CI, Electron, mobile layout and some browser codec combinations have not been manually tested locally.