Files
simplex-chat/apps/multiplatform/spec/services/files.md
2026-02-26 17:54:44 +00:00

14 KiB

File Transfer Service

Table of Contents

  1. Overview
  2. File Size Constants
  3. CryptoFile
  4. File Storage Paths
  5. API Commands
  6. Auto-Receive Logic
  7. Source Files

Executive Summary

SimpleX Chat uses two file transfer mechanisms: inline SMP transfers for small files (embedded in message bodies) and XFTP (eXtended File Transfer Protocol) for larger files up to 1 GB. Files are optionally encrypted at rest using CryptoFile functions backed by the chat core's native crypto library. File storage paths are platform-specific: Android uses Context.dataDir-based directories while Desktop uses platform-appropriate data directories (XDG on Linux, AppData on Windows, Application Support on macOS). Auto-receive logic automatically accepts images, voice messages, and videos below configurable size thresholds.


1. Overview

File transfer decision logic:

  • Inline (SMP): Files small enough to be base64-encoded and embedded directly in an SMP message body. The practical limit is defined by MAX_IMAGE_SIZE (255 KB) for compressed images. The maximum SMP inline size is MAX_FILE_SIZE_SMP (~7.6 MB).
  • XFTP: For files exceeding the inline threshold, up to MAX_FILE_SIZE_XFTP (1 GB). XFTP uses dedicated file relay servers with chunked, encrypted transfers.

The receiveFile / receiveFiles API commands handle both protocols transparently -- the chat core selects the appropriate transfer mechanism based on file metadata received from the sender.


2. File Size Constants

Defined in Utils.kt:

Constant Value Human-Readable Line Purpose
MAX_IMAGE_SIZE 261,120 255 KB L118 Inline image compression target
MAX_IMAGE_SIZE_AUTO_RCV 522,240 510 KB L119 Auto-receive threshold for images (2 * MAX_IMAGE_SIZE)
MAX_VOICE_SIZE_AUTO_RCV 522,240 510 KB L120 Auto-receive threshold for voice messages (2 * MAX_IMAGE_SIZE)
MAX_VIDEO_SIZE_AUTO_RCV 1,047,552 1023 KB L121 Auto-receive threshold for video
MAX_VOICE_MILLIS_FOR_SENDING 300,000 5 min L123 Maximum voice message duration
MAX_FILE_SIZE_SMP 8,000,000 ~7.6 MB L125 Maximum SMP inline file size
MAX_FILE_SIZE_XFTP 1,073,741,824 1 GB L127 Maximum XFTP transfer size
MAX_FILE_SIZE_LOCAL Long.MAX_VALUE Unlimited L129 Local file protocol (no size limit)

The getMaxFileSize() function (Utils.kt) selects the limit based on FileProtocol:

FileProtocol.XFTP -> MAX_FILE_SIZE_XFTP
FileProtocol.SMP  -> MAX_FILE_SIZE_SMP
FileProtocol.LOCAL -> MAX_FILE_SIZE_LOCAL

3. CryptoFile

CryptoFile.kt (62 lines)

Provides encrypted file I/O backed by the chat core's native cryptography (via JNI/JNA calls to chatWriteFile, chatReadFile, chatEncryptFile, chatDecryptFile).

Data types

@Serializable
sealed class WriteFileResult {
  @SerialName("result") data class Result(val cryptoArgs: CryptoFileArgs): WriteFileResult()
  @SerialName("error") data class Error(val writeError: String): WriteFileResult()
}

CryptoFileArgs contains fileKey and fileNonce -- the symmetric encryption key and nonce for AES-GCM encryption.

Functions

Function Line Signature Description
writeCryptoFile L24 (path: String, data: ByteArray): CryptoFileArgs Writes data to an encrypted file via a direct ByteBuffer. Returns the generated key and nonce. Requires initialized ChatController.
readCryptoFile L36 (path: String, cryptoArgs: CryptoFileArgs): ByteArray Reads and decrypts a file given its key and nonce. Returns the plaintext bytes. Throws on error (status != 0).
encryptCryptoFile L47 (fromPath: String, toPath: String): CryptoFileArgs Encrypts an existing plaintext file to a new encrypted file. Returns the generated key and nonce.
decryptCryptoFile L57 (fromPath: String, cryptoArgs: CryptoFileArgs, toPath: String) Decrypts an encrypted file to a plaintext output file. Throws on non-empty error string.

All functions delegate to native C library functions through the chat core JNI bridge.


4. File Storage Paths

Common expect declarations

Files.kt (191 lines, commonMain)

Property Line Description
dataDir L18 Root application data directory
tmpDir L19 Temporary files directory
filesDir L20 Base files directory
appFilesDir L21 Application files (chat attachments)
wallpapersDir L22 Theme wallpaper images
coreTmpDir L23 Temporary files for the chat core
dbAbsolutePrefixPath L24 Database file path prefix
preferencesDir L25 Preferences/config directory
databaseExportDir L35 Temporary DB archive storage for export
remoteHostsDir L37 Remote host connection data

Android implementation

Files.android.kt (79 lines)

Property Value
dataDir androidAppContext.dataDir
tmpDir androidAppContext.getDir("temp", MODE_PRIVATE)
filesDir dataDir/files
appFilesDir dataDir/files/app_files
wallpapersDir dataDir/files/assets/wallpapers
coreTmpDir dataDir/files/temp_files
dbAbsolutePrefixPath dataDir/files
preferencesDir dataDir/shared_prefs
databaseExportDir androidAppContext.cacheDir
remoteHostsDir tmpDir/remote_hosts

Desktop implementation

Files.desktop.kt (116 lines)

Property Value
dataDir desktopPlatform.dataPath (XDG_DATA_HOME on Linux, AppData on Windows, Application Support on macOS)
tmpDir java.io.tmpdir/simplex (deleted on exit)
filesDir dataDir/simplex_v1_files
appFilesDir Same as filesDir
wallpapersDir dataDir/simplex_v1_assets/wallpapers
coreTmpDir dataDir/tmp
dbAbsolutePrefixPath dataDir/simplex_v1
preferencesDir desktopPlatform.configPath
databaseExportDir Same as tmpDir
remoteHostsDir dataDir/remote_hosts

Helper functions (common)

Function Line Description
getAppFilePath L81 Resolves file path considering remote hosts
getWallpaperFilePath L91 Resolves wallpaper image path, creates parent directories
getLoadedFilePath L105 Returns path if file exists and is fully loaded
getLoadedFileSource L115 Returns CryptoFile source if file is loaded
readThemeOverrides L125 Reads theme overrides from themes.yaml
writeThemeOverrides L151 Atomically writes theme overrides to themes.yaml
copyFileToFile L47 Copies a File to a URI destination with toast feedback
copyBytesToFile L63 Copies a ByteArrayInputStream to a URI destination

5. API Commands

Defined in SimpleXAPI.kt:

Function Line Signature Description
receiveFiles L1946 (rhId, user, fileIds, userApprovedRelays, auto) Receive multiple files. Sends CC.ReceiveFile for each ID. Handles relay approval workflow: collects unapproved files, shows alert, re-calls with userApprovedRelays=true. Respects privacyEncryptLocalFiles preference.
receiveFile L2062 (rhId, user, fileId, userApprovedRelays, auto) Delegates to receiveFiles with a single-element list.
cancelFile L2072 (rh, user, fileId) Cancels an in-progress file transfer (send or receive). Cleans up the local file.
apiCancelFile L2080 (rh, fileId, ctrl?) Low-level cancel. Returns AChatItem? on success (SndFileCancelled or RcvFileCancelled).
uploadStandaloneFile L1916 (user, file, ctrl?) Upload a standalone file (for database migration). Returns FileTransferMeta? with XFTP link.
downloadStandaloneFile L1926 (user, url, file, ctrl?) Download a standalone file from an XFTP URL. Returns RcvFileTransfer?.

6. Auto-Receive Logic

Located in SimpleXAPI.kt within the CR.NewChatItems handler:

if (file != null &&
    appPrefs.privacyAcceptImages.get() &&
    ((mc is MsgContent.MCImage && file.fileSize <= MAX_IMAGE_SIZE_AUTO_RCV)
        || (mc is MsgContent.MCVideo && file.fileSize <= MAX_VIDEO_SIZE_AUTO_RCV)
        || (mc is MsgContent.MCVoice && file.fileSize <= MAX_VOICE_SIZE_AUTO_RCV
            && file.fileStatus !is CIFileStatus.RcvAccepted))
) {
    receiveFile(rhId, r.user, file.fileId, auto = true)
}

Conditions for auto-receive:

  1. The privacyAcceptImages preference is enabled (user opt-in).
  2. The content type and size match one of:
    • Images (MCImage): file size <= 510 KB (MAX_IMAGE_SIZE_AUTO_RCV)
    • Video (MCVideo): file size <= 1023 KB (MAX_VIDEO_SIZE_AUTO_RCV)
    • Voice (MCVoice): file size <= 510 KB (MAX_VOICE_SIZE_AUTO_RCV) AND file is not already accepted
  3. The file has a non-null file attachment.

When auto = true, relay approval alerts are suppressed (the file is silently received).


7. Source Files

File Path Lines Description
CryptoFile.kt common/src/commonMain/.../model/CryptoFile.kt 62 Encrypted file read/write via native crypto
Files.kt common/src/commonMain/.../platform/Files.kt 191 Common file path declarations, theme I/O, file helpers
Files.android.kt common/src/androidMain/.../platform/Files.android.kt 79 Android file path implementations
Files.desktop.kt common/src/desktopMain/.../platform/Files.desktop.kt 116 Desktop file path implementations
Utils.kt common/src/commonMain/.../views/helpers/Utils.kt -- File size constants (L117--L128), getMaxFileSize() (L442)
SimpleXAPI.kt common/src/commonMain/.../model/SimpleXAPI.kt -- File transfer API commands (L1911--L2085), auto-receive (L2690)