mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-25 12:04:22 +00:00
10 KiB
10 KiB
File Transfer Flow
Related spec: spec/services/files.md
Overview
File and media sharing in SimpleX Chat iOS. Small files are sent inline within SMP messages; large files use the XFTP (eXtended File Transfer Protocol) for chunked, encrypted uploads up to 1GB. All files are encrypted end-to-end. Optional local encryption protects downloaded files at rest using AES via CryptoFile.
Prerequisites
- Established contact or group conversation
- For sending: photo library or file picker access permission
- For receiving: sufficient device storage
- XFTP relay servers configured (default servers or custom)
Size Limits
| Category | Limit | Constant |
|---|---|---|
| Inline image (compressed) | 255 KB | MAX_IMAGE_SIZE = 261,120 bytes |
| Auto-receive image | 510 KB | MAX_IMAGE_SIZE_AUTO_RCV = MAX_IMAGE_SIZE * 2 |
| Auto-receive voice | 510 KB | MAX_VOICE_SIZE_AUTO_RCV = MAX_IMAGE_SIZE * 2 |
| Auto-receive video | 1,023 KB | MAX_VIDEO_SIZE_AUTO_RCV = 1,047,552 bytes |
| Max file via XFTP | 1 GB | MAX_FILE_SIZE_XFTP = 1,073,741,824 bytes |
| Max file via SMP | ~8 MB | MAX_FILE_SIZE_SMP = 8,000,000 bytes |
| Max voice message length | 5 min | MAX_VOICE_MESSAGE_LENGTH = 300s |
Step-by-Step Processes
1. Send Image
- User taps the attachment button in
ComposeViewand selects an image. ComposeImageViewdisplays the selected image preview.- Image is compressed to fit within
MAX_IMAGE_SIZE(255KB). ComposedMessageis built:ComposedMessage( fileSource: CryptoFile(filePath: compressedImagePath), msgContent: .image(text: captionText, image: base64Thumbnail) )apiSendMessages(type:id:scope:composedMessages:)is called.- For images <=255KB: sent inline within the SMP message.
- For larger images: XFTP upload is used (see XFTP transfer below).
- Recipient auto-receives images up to 510KB (
MAX_IMAGE_SIZE_AUTO_RCV).
2. Send Video
- User picks a video from the library.
- Thumbnail is generated from the first frame.
- Video duration is calculated.
ComposedMessageis built:ComposedMessage( fileSource: CryptoFile(filePath: videoFilePath), msgContent: .video(text: captionText, image: base64Thumbnail, duration: durationSeconds) )apiSendMessages(...)is called.- Video files are typically >255KB, so XFTP upload is used.
- Recipient auto-receives videos up to 1,023KB (
MAX_VIDEO_SIZE_AUTO_RCV). CIVideoViewdisplays thumbnail with play button; video downloads on tap if not auto-received.
3. Send File
- User taps the attachment button and selects a document via the system file picker.
ComposeFileViewshows the file name and size.ComposedMessageis built:ComposedMessage( fileSource: CryptoFile(filePath: filePath), msgContent: .file(fileName) )apiSendMessages(...)is called.- If file <=255KB: sent inline via SMP.
- If file >255KB and <=1GB: uploaded via XFTP.
- Files >1GB: rejected (prevented in UI).
CIFileViewdisplays file icon, name, and size for the recipient.
4. Send Voice Message
- User taps and holds the microphone button in
ComposeView. AudioRecPlayrecords audio to a temporary file.ComposeVoiceViewshows recording waveform and duration.- On release (or tapping stop), recording ends.
- Duration is checked against
MAX_VOICE_MESSAGE_LENGTH(5 minutes / 300 seconds). ComposedMessageis built:ComposedMessage( fileSource: CryptoFile(filePath: voiceFilePath), msgContent: .voice(text: "", duration: durationSeconds) )apiSendMessages(...)is called.- Voice messages <=510KB are sent inline.
- Recipient auto-receives voice up to 510KB (
MAX_VOICE_SIZE_AUTO_RCV). CIVoiceViewrenders waveform with playback controls.
5. Receive File
- Core receives a message with a file reference via SMP.
ChatEvent.newChatItemsdelivers the chat item with file metadata.- Auto-receive logic checks:
- File type and size against auto-receive thresholds.
- User's auto-receive preferences.
- If auto-received or user taps "Download":
func receiveFile(user: any UserLike, fileId: Int64, userApprovedRelays: Bool = false, auto: Bool = false) async - Internally calls
receiveFiles(user:fileIds:userApprovedRelays:auto:). - Sends
ChatCommand.receiveFile(fileId:userApprovedRelays:encrypted:inline:). encryptedis determined byprivacyEncryptLocalFilesGroupDefault.userApprovedRelayscontrols whether unknown XFTP relay servers are trusted.- On success:
ChatResponse2.rcvFileAccepted(user, chatItem)-- file download begins. - On sender cancelled:
ChatResponse2.rcvFileAcceptedSndCancelled(user, rcvFileTransfer). - Download progress is tracked and shown in the UI.
- Completed files are stored in the app's
Documents/files/directory.
6. XFTP Transfer (Large Files)
Upload (sender side):
- File is encrypted locally with a random symmetric key.
- Encrypted file is split into chunks.
- Chunks are uploaded to one or more XFTP relay servers.
- A file description (URI with encryption key and chunk locations) is created.
- The file description is sent to the recipient via the SMP message.
Download (recipient side):
- Recipient receives the file description via SMP.
- Chunks are downloaded from XFTP relay servers.
- Chunks are reassembled and decrypted locally.
- File is available at the local path.
Standalone file operations (used for database migration):
uploadStandaloneFile(user:file:ctrl:)-- upload without a chat messagedownloadStandaloneFile(user:url:file:ctrl:)-- download from a standalone URLstandaloneFileInfo(url:ctrl:)-- get metadata for a standalone file URL
7. Local File Encryption
- If
privacyEncryptLocalFilesGroupDefaultis enabled in privacy settings:- Downloaded files are encrypted at rest using AES via
CryptoFile. CryptoFilewraps a file path with encryption metadata.
- Downloaded files are encrypted at rest using AES via
- Encryption key is derived and stored securely.
- Files are decrypted on-the-fly when accessed for viewing/playback.
- This protects files even if the device storage is accessed externally.
8. Unknown Relay Server Approval
- When receiving a file, XFTP relay servers are checked against known/approved servers.
- If unknown servers are detected:
ChatError.error(.fileNotApproved(fileId, unknownServers)). - If not auto-receiving, user is shown an alert:
- "Unknown servers! Without Tor or VPN, your IP address will be visible to these XFTP relays: [server list]."
- Option to "Download" (approve) or cancel.
- On approval:
receiveFiles(user:fileIds:userApprovedRelays: true)retries with approval. - If
privacyAskToApproveRelaysGroupDefaultis disabled, relays are auto-approved.
Data Structures
| Type | Location | Description |
|---|---|---|
CryptoFile |
SimpleXChat/CryptoFile.swift |
File path with optional encryption key and nonce for local AES encryption |
MsgContent.image |
SimpleXChat/ChatTypes.swift |
.image(text: String, image: String) -- text caption + base64 thumbnail |
MsgContent.video |
SimpleXChat/ChatTypes.swift |
.video(text: String, image: String, duration: Int) -- caption + thumbnail + duration |
MsgContent.voice |
SimpleXChat/ChatTypes.swift |
.voice(text: String, duration: Int) -- empty text + duration in seconds |
MsgContent.file |
SimpleXChat/ChatTypes.swift |
.file(String) -- file name |
ComposedMessage |
SimpleXChat/APITypes.swift |
Outgoing message with fileSource, quotedItemId, msgContent, mentions |
FileTransferMeta |
SimpleXChat/ChatTypes.swift |
Metadata for an ongoing file transfer |
RcvFileTransfer |
SimpleXChat/ChatTypes.swift |
State of a file being received |
MigrationFileLinkData |
Used for standalone file transfers during database migration |
Error Cases
| Error | Cause | Handling |
|---|---|---|
fileNotApproved(fileId, unknownServers) |
Unknown XFTP relay servers | Alert with option to approve and retry |
fileCancelled |
File transfer was cancelled | Silently ignored in receiveFiles |
fileAlreadyReceiving |
Duplicate receive request | Silently ignored |
rcvFileAcceptedSndCancelled |
Sender cancelled after acceptance | Alert: "Sender cancelled file transfer" |
| File too large | Exceeds 1GB XFTP limit | Prevented in UI picker |
| Network errors | XFTP server unreachable | Standard retry mechanism |
| Storage full | Insufficient device storage | System-level error |
Key Files
| File | Purpose |
|---|---|
SimpleXChat/FileUtils.swift |
File size constants, path utilities, database file management |
SimpleXChat/CryptoFile.swift |
Local file encryption/decryption with AES |
SimpleXChat/ImageUtils.swift |
Image compression and thumbnail generation |
Shared/Views/Chat/ComposeMessage/ComposeView.swift |
File/media attachment selection and composition |
Shared/Views/Chat/ComposeMessage/ComposeImageView.swift |
Image preview in compose area |
Shared/Views/Chat/ComposeMessage/ComposeFileView.swift |
File preview in compose area |
Shared/Views/Chat/ComposeMessage/ComposeVoiceView.swift |
Voice recording UI with waveform |
Shared/Views/Chat/ChatItem/CIFileView.swift |
File message display: icon, name, size, download action |
Shared/Views/Chat/ChatItem/CIImageView.swift |
Image message display: thumbnail, full-screen tap |
Shared/Views/Chat/ChatItem/CIVideoView.swift |
Video message display: thumbnail, play button, inline playback |
Shared/Views/Chat/ChatItem/CIVoiceView.swift |
Voice message display: waveform, playback controls |
Shared/Views/Chat/ChatItem/FramedCIVoiceView.swift |
Voice message inside a framed (quoted/forwarded) context |
Shared/Views/Chat/ChatItem/FullScreenMediaView.swift |
Full-screen image/video viewer |
Shared/Model/SimpleXAPI.swift |
apiSendMessages, receiveFile, receiveFiles, uploadStandaloneFile, downloadStandaloneFile |
Shared/Model/AudioRecPlay.swift |
Audio recording and playback engine for voice messages |
Related Specifications
apps/ios/product/README.md-- Product overview: Messaging capability (file sharing)apps/ios/product/flows/messaging.md-- File transfer is part of the message send flowapps/ios/product/views/chat.md-- Chat view file/media display