mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-23 21:33:47 +00:00
multiplatform: product specification (#6655)
This commit is contained in:
1 parent
3c27b5a29f
commit
a09acda329
61 files changed
+9501
No files matched your search
@@ -0,0 +1,393 @@
|
||||
# Database & Storage
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Overview](#1-overview)
|
||||
2. [Database Files & Paths](#2-database-files--paths)
|
||||
3. [Haskell Store Modules](#3-haskell-store-modules)
|
||||
4. [Migrations](#4-migrations)
|
||||
5. [Database Encryption](#5-database-encryption)
|
||||
6. [File Storage](#6-file-storage)
|
||||
7. [Export & Import](#7-export--import)
|
||||
8. [Source Files](#8-source-files)
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
SimpleX Chat uses **two SQLite databases** managed entirely by the Haskell core. Kotlin code **never reads or writes the databases directly** -- all data access goes through the JNI command/response protocol defined in [SimpleXAPI.kt](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt).
|
||||
|
||||
The two databases are:
|
||||
|
||||
| Database | Suffix | Contents |
|
||||
|----------|--------|----------|
|
||||
| Chat database | `_chat.db` | Users, contacts, groups, messages, files metadata, settings |
|
||||
| Agent database | `_agent.db` | SMP/XFTP agent state: connections, queues, encryption keys, delivery tracking |
|
||||
|
||||
Both databases are created and migrated by the `chatMigrateInit` JNI function. The Kotlin layer handles:
|
||||
- Providing the correct file path prefix (`dbAbsolutePrefixPath`)
|
||||
- Providing the encryption key
|
||||
- Interpreting migration results (`DBMigrationResult`)
|
||||
- Exposing API functions that proxy to Haskell store operations
|
||||
|
||||
---
|
||||
|
||||
## 2. Database Files & Paths
|
||||
|
||||
### Expect Declarations
|
||||
|
||||
The common module declares platform-dependent paths as `expect` values in [Files.kt](../common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt):
|
||||
|
||||
```kotlin
|
||||
expect val dataDir: File // L18
|
||||
expect val tmpDir: File // L19
|
||||
expect val filesDir: File // L20
|
||||
expect val appFilesDir: File // L21
|
||||
expect val wallpapersDir: File // L22
|
||||
expect val coreTmpDir: File // L23
|
||||
expect val dbAbsolutePrefixPath: String // L24
|
||||
expect val preferencesDir: File // L25
|
||||
expect val preferencesTmpDir: File // L26
|
||||
|
||||
expect val chatDatabaseFileName: String // L28
|
||||
expect val agentDatabaseFileName: String // L29
|
||||
|
||||
expect val databaseExportDir: File // L35
|
||||
expect val remoteHostsDir: File // L37
|
||||
```
|
||||
|
||||
### Android Actual Values
|
||||
|
||||
From [Files.android.kt](../common/src/androidMain/kotlin/chat/simplex/common/platform/Files.android.kt):
|
||||
|
||||
| Variable | Value | Notes |
|
||||
|----------|-------|-------|
|
||||
| `dataDir` | `androidAppContext.dataDir` | `/data/data/<package>/` |
|
||||
| `tmpDir` | `getDir("temp", MODE_PRIVATE)` | Private temp directory |
|
||||
| `filesDir` | `dataDir/files` | Parent for all file storage |
|
||||
| `appFilesDir` | `filesDir/app_files` | User-visible chat file attachments |
|
||||
| `wallpapersDir` | `filesDir/assets/wallpapers` | Custom wallpaper images |
|
||||
| `coreTmpDir` | `filesDir/temp_files` | Haskell core temp directory |
|
||||
| `dbAbsolutePrefixPath` | `dataDir/files` | Prefix: core appends `_chat.db` / `_agent.db` |
|
||||
| `chatDatabaseFileName` | `"files_chat.db"` | Full filename: `files_chat.db` |
|
||||
| `agentDatabaseFileName` | `"files_agent.db"` | Full filename: `files_agent.db` |
|
||||
| `databaseExportDir` | `androidAppContext.cacheDir` | Temp location for archive export |
|
||||
| `remoteHostsDir` | `tmpDir/remote_hosts` | Remote host file staging |
|
||||
| `preferencesDir` | `dataDir/shared_prefs` | Android SharedPreferences directory |
|
||||
|
||||
### Desktop Actual Values
|
||||
|
||||
From [Files.desktop.kt](../common/src/desktopMain/kotlin/chat/simplex/common/platform/Files.desktop.kt):
|
||||
|
||||
| Variable | Value | Notes |
|
||||
|----------|-------|-------|
|
||||
| `dataDir` | `desktopPlatform.dataPath` | XDG_DATA_HOME (Linux), AppData (Windows), Application Support (macOS) |
|
||||
| `tmpDir` | `java.io.tmpdir/simplex` | System temp with `deleteOnExit` |
|
||||
| `filesDir` | `dataDir/simplex_v1_files` | Flat file storage |
|
||||
| `appFilesDir` | Same as `filesDir` | No subdirectory on desktop |
|
||||
| `wallpapersDir` | `dataDir/simplex_v1_assets/wallpapers` | Custom wallpaper images |
|
||||
| `coreTmpDir` | `dataDir/tmp` | Haskell core temp directory |
|
||||
| `dbAbsolutePrefixPath` | `dataDir/simplex_v1` | Prefix: core appends `_chat.db` / `_agent.db` |
|
||||
| `chatDatabaseFileName` | `"simplex_v1_chat.db"` | Full filename: `simplex_v1_chat.db` |
|
||||
| `agentDatabaseFileName` | `"simplex_v1_agent.db"` | Full filename: `simplex_v1_agent.db` |
|
||||
| `databaseExportDir` | Same as `tmpDir` | Temp location for archive export |
|
||||
| `remoteHostsDir` | `dataDir/remote_hosts` | Remote host file staging |
|
||||
| `preferencesDir` | `desktopPlatform.configPath` | Platform config directory |
|
||||
|
||||
### Resulting Database Paths
|
||||
|
||||
| Platform | Chat DB | Agent DB |
|
||||
|----------|---------|----------|
|
||||
| Android | `/data/data/<pkg>/files_chat.db` | `/data/data/<pkg>/files_agent.db` |
|
||||
| Desktop (Linux) | `~/.local/share/simplex/simplex_v1_chat.db` | `~/.local/share/simplex/simplex_v1_agent.db` |
|
||||
| Desktop (macOS) | `~/Library/Application Support/simplex/simplex_v1_chat.db` | ... |
|
||||
| Desktop (Windows) | `%APPDATA%/simplex/simplex_v1_chat.db` | ... |
|
||||
|
||||
---
|
||||
|
||||
## 3. Haskell Store Modules
|
||||
|
||||
The Haskell core organizes database access into store modules. Kotlin code invokes these indirectly through `CC` commands. The store modules are:
|
||||
|
||||
| Module | Path | Responsibilities |
|
||||
|--------|------|-----------------|
|
||||
| `Messages.hs` | `src/Simplex/Chat/Store/Messages.hs` | Message CRUD, chat items, reactions, delivery statuses, TTL cleanup |
|
||||
| `Groups.hs` | `src/Simplex/Chat/Store/Groups.hs` | Group profiles, membership, roles, invitations, group links |
|
||||
| `Direct.hs` | `src/Simplex/Chat/Store/Direct.hs` | Contact management, direct connections, contact requests |
|
||||
| `Files.hs` | `src/Simplex/Chat/Store/Files.hs` | File transfer metadata, XFTP state, standalone files |
|
||||
| `Profiles.hs` | `src/Simplex/Chat/Store/Profiles.hs` | User profiles, display names, address book |
|
||||
| `Connections.hs` | `src/Simplex/Chat/Store/Connections.hs` | SMP agent connections, pending connections, server switches |
|
||||
|
||||
All store operations execute within SQLite transactions managed by the Haskell core. The Kotlin layer has no direct knowledge of table schemas or SQL queries.
|
||||
|
||||
---
|
||||
|
||||
## 4. Migrations
|
||||
|
||||
### JNI Entry Point
|
||||
|
||||
Database migration is triggered by the `chatMigrateInit` external function ([Core.kt#L25](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L25)):
|
||||
|
||||
```kotlin
|
||||
external fun chatMigrateInit(dbPath: String, dbKey: String, confirm: String): Array<Any>
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `dbPath` -- the `dbAbsolutePrefixPath` (core appends `_chat.db` and `_agent.db`)
|
||||
- `dbKey` -- encryption passphrase (empty string = unencrypted)
|
||||
- `confirm` -- migration confirmation mode: `"error"`, `"yesUp"`, or `"yesUpDown"`
|
||||
|
||||
**Returns:** `Array<Any>` where:
|
||||
- `[0]` -- JSON string encoding a `DBMigrationResult`
|
||||
- `[1]` -- `ChatCtrl` handle (Long) if migration succeeded
|
||||
|
||||
### Migration Flow in `initChatController`
|
||||
|
||||
The full initialization sequence is in [Core.kt#L62](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L62):
|
||||
|
||||
1. Obtain the DB encryption key from `DatabaseUtils.useDatabaseKey()`.
|
||||
2. Determine the confirmation mode (default: `YesUp`; developer mode with confirm upgrades: `Error`).
|
||||
3. Call `chatMigrateInit(dbAbsolutePrefixPath, dbKey, "error")` -- first attempt with `Error` to detect pending migrations.
|
||||
4. Parse the result as `DBMigrationResult`.
|
||||
5. If the result is `ErrorMigration` with an `Upgrade` error and confirmation allows it, re-run `chatMigrateInit` with the appropriate confirmation (`"yesUp"`).
|
||||
6. If `OK`, store the `ChatCtrl` handle, set `chatDbEncrypted`, and proceed to start the chat.
|
||||
7. If not `OK`, handle special case: if the `newDatabaseInitialized` preference is not set AND the database was only partially initialized (single DB file exists), remove both files and retry once.
|
||||
|
||||
<a id="DBMigrationResult"></a>
|
||||
|
||||
### DBMigrationResult
|
||||
|
||||
Defined in [DatabaseUtils.kt#L79](../common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DatabaseUtils.kt#L79):
|
||||
|
||||
```kotlin
|
||||
sealed class DBMigrationResult {
|
||||
object OK // Migration succeeded
|
||||
object InvalidConfirmation // Invalid confirmation parameter
|
||||
data class ErrorNotADatabase(val dbFile: String) // File exists but is not a valid database
|
||||
data class ErrorMigration(val dbFile: String, // Migration error with details
|
||||
val migrationError: MigrationError)
|
||||
data class ErrorSQL(val dbFile: String, // SQL error during migration
|
||||
val migrationSQLError: String)
|
||||
object ErrorKeychain // Keychain/keystore error
|
||||
data class Unknown(val json: String) // Unparseable response
|
||||
}
|
||||
```
|
||||
|
||||
### MigrationError
|
||||
|
||||
```kotlin
|
||||
sealed class MigrationError {
|
||||
class Upgrade(val upMigrations: List<UpMigration>) // Pending forward migrations
|
||||
class Downgrade(val downMigrations: List<String>) // Database is newer than app
|
||||
class Error(val mtrError: MTRError) // Conflict or missing migrations
|
||||
}
|
||||
```
|
||||
|
||||
### MigrationConfirmation
|
||||
|
||||
```kotlin
|
||||
enum class MigrationConfirmation(val value: String) {
|
||||
YesUp("yesUp"), // Auto-confirm forward migrations
|
||||
YesUpDown("yesUpDown"), // Auto-confirm both directions (not used in UI)
|
||||
Error("error") // Report errors without running migrations
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Database Encryption
|
||||
|
||||
### Encryption API
|
||||
|
||||
Two API functions manage database encryption, both in [SimpleXAPI.kt](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt):
|
||||
|
||||
| Function | Parameters | Description | Line |
|
||||
|----------|-----------|-------------|------|
|
||||
| `apiStorageEncryption` | `currentKey: String, newKey: String` | Change or set the database encryption passphrase | [L999](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L999) |
|
||||
| `testStorageEncryption` | `key: String, ctrl: ChatCtrl?` | Test whether a given key can decrypt the database | [L1006](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L1006) |
|
||||
|
||||
Both delegate to the Haskell core via `CC.ApiStorageEncryption(DBEncryptionConfig)` and `CC.TestStorageEncryption(key)` respectively.
|
||||
|
||||
<a id="DBEncryptionConfig"></a>
|
||||
|
||||
`DBEncryptionConfig` ([SimpleXAPI.kt#L4166](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L4166)):
|
||||
|
||||
```kotlin
|
||||
class DBEncryptionConfig(val currentKey: String, val newKey: String)
|
||||
```
|
||||
|
||||
### Passphrase Storage -- CryptorInterface
|
||||
|
||||
The `CryptorInterface` ([Cryptor.kt](../common/src/commonMain/kotlin/chat/simplex/common/platform/Cryptor.kt)) provides platform-specific key encryption for storing the DB passphrase at rest:
|
||||
|
||||
```kotlin
|
||||
interface CryptorInterface {
|
||||
fun decryptData(data: ByteArray, iv: ByteArray, alias: String): String?
|
||||
fun encryptText(text: String, alias: String): Pair<ByteArray, ByteArray>
|
||||
fun deleteKey(alias: String)
|
||||
}
|
||||
|
||||
expect val cryptor: CryptorInterface
|
||||
```
|
||||
|
||||
### Android Implementation
|
||||
|
||||
[Cryptor.android.kt](../common/src/androidMain/kotlin/chat/simplex/common/platform/Cryptor.android.kt):
|
||||
|
||||
- Uses **Android KeyStore** (`"AndroidKeyStore"` provider)
|
||||
- Algorithm: **AES/GCM/NoPadding** (128-bit authentication tag)
|
||||
- Keys are hardware-backed when available
|
||||
- On decryption failure with a random initial passphrase, throws to prevent overwriting
|
||||
- Shows user alerts for keychain errors
|
||||
|
||||
```kotlin
|
||||
internal class Cryptor: CryptorInterface {
|
||||
private var keyStore: KeyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
|
||||
// AES-GCM encryption/decryption using AndroidKeyStore-managed keys
|
||||
}
|
||||
```
|
||||
|
||||
### Desktop Implementation
|
||||
|
||||
[Cryptor.desktop.kt](../common/src/desktopMain/kotlin/chat/simplex/common/platform/Cryptor.desktop.kt):
|
||||
|
||||
- **Placeholder/no-op implementation** -- data is returned as-is
|
||||
- No actual encryption of the stored passphrase on desktop
|
||||
- `decryptData` returns `String(data)` without decryption
|
||||
- `encryptText` returns the raw bytes without encryption
|
||||
|
||||
```kotlin
|
||||
actual val cryptor: CryptorInterface = object : CryptorInterface {
|
||||
override fun decryptData(data: ByteArray, iv: ByteArray, alias: String): String? = String(data)
|
||||
override fun encryptText(text: String, alias: String) = text.toByteArray() to text.toByteArray()
|
||||
override fun deleteKey(alias: String) {}
|
||||
}
|
||||
```
|
||||
|
||||
### Passphrase Management
|
||||
|
||||
`DatabaseUtils` ([DatabaseUtils.kt](../common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DatabaseUtils.kt)) provides:
|
||||
|
||||
- `ksDatabasePassword` -- encrypted passphrase stored in platform preferences (SharedPreferences on Android, file-based on desktop)
|
||||
- `useDatabaseKey()` -- retrieves the passphrase, decrypting it via `CryptorInterface`
|
||||
- `randomDatabasePassword()` -- generates a 32-byte random passphrase (Base64-encoded) for initial database creation
|
||||
|
||||
The flow:
|
||||
1. On first launch, `randomDatabasePassword()` generates a key.
|
||||
2. `CryptorInterface.encryptText()` encrypts the key for storage.
|
||||
3. The encrypted (data, IV) pair is saved to preferences via `ksDatabasePassword`.
|
||||
4. On subsequent launches, `ksDatabasePassword.get()` retrieves the encrypted pair, and `CryptorInterface.decryptData()` recovers the plaintext key.
|
||||
5. The key is passed to `chatMigrateInit` to open the encrypted SQLite databases.
|
||||
|
||||
---
|
||||
|
||||
## 6. File Storage
|
||||
|
||||
### Directory Layout
|
||||
|
||||
Declared in [Files.kt](../common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt) with platform-specific implementations:
|
||||
|
||||
| Directory | Variable | Android Path | Desktop Path | Purpose |
|
||||
|-----------|----------|-------------|--------------|---------|
|
||||
| App files | `appFilesDir` | `dataDir/files/app_files` | `dataDir/simplex_v1_files` | Chat file attachments (images, videos, documents) |
|
||||
| Wallpapers | `wallpapersDir` | `dataDir/files/assets/wallpapers` | `dataDir/simplex_v1_assets/wallpapers` | Custom chat wallpaper images |
|
||||
| Core temp | `coreTmpDir` | `dataDir/files/temp_files` | `dataDir/tmp` | Haskell core temporary files (in-progress transfers) |
|
||||
| App temp | `tmpDir` | `getDir("temp", MODE_PRIVATE)` | `java.io.tmpdir/simplex` | Application-level temporary files |
|
||||
| Remote hosts | `remoteHostsDir` | `tmpDir/remote_hosts` | `dataDir/remote_hosts` | Files staged for remote host sessions |
|
||||
| DB export | `databaseExportDir` | `androidAppContext.cacheDir` | Same as `tmpDir` | Temporary storage for database archive ZIP |
|
||||
| Preferences | `preferencesDir` | `dataDir/shared_prefs` | `desktopPlatform.configPath` | User preferences, theme YAML |
|
||||
| Migration temp | `getMigrationTempFilesDirectory()` | `dataDir/migration_temp_files` | `dataDir/migration_temp_files` | Temporary files during database migration |
|
||||
|
||||
### File Path Resolution
|
||||
|
||||
Files referenced by chat items use `CryptoFile` (optional encryption metadata + relative path). Path resolution is handled by helper functions in [Files.kt](../common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt):
|
||||
|
||||
- `getAppFilePath(fileName)` ([L81](../common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt#L81)) -- resolves to `appFilesDir/fileName` for local, or `remoteHostsDir/<storePath>/simplex_v1_files/fileName` for remote hosts
|
||||
- `getWallpaperFilePath(fileName)` ([L91](../common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt#L91)) -- resolves wallpaper paths similarly
|
||||
- `getLoadedFilePath(file)` ([L105](../common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt#L105)) -- returns the full path if the file is downloaded and ready
|
||||
|
||||
### Local File Encryption
|
||||
|
||||
The `apiSetEncryptLocalFiles(enable)` command ([SimpleXAPI.kt#L967](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L967)) tells the Haskell core to encrypt files stored in `appFilesDir`. When enabled, files are written as `CryptoFile` with a random AES key and nonce. The JNI functions `chatEncryptFile` and `chatDecryptFile` ([Core.kt#L39-L40](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L39)) handle the actual crypto operations.
|
||||
|
||||
---
|
||||
|
||||
## 7. Export & Import
|
||||
|
||||
### API Functions
|
||||
|
||||
| Function | CC Command | CR Response | Line |
|
||||
|----------|-----------|-------------|------|
|
||||
| `apiExportArchive(config)` | `CC.ApiExportArchive(config)` | `CR.ArchiveExported(archiveErrors)` | [SimpleXAPI.kt#L981](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L981) |
|
||||
| `apiImportArchive(config)` | `CC.ApiImportArchive(config)` | `CR.ArchiveImported(archiveErrors)` | [SimpleXAPI.kt#L987](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L987) |
|
||||
| `apiDeleteStorage()` | `CC.ApiDeleteStorage()` | `CR.CmdOk` | [SimpleXAPI.kt#L993](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L993) |
|
||||
|
||||
### ArchiveConfig
|
||||
|
||||
Defined at [SimpleXAPI.kt#L4162](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L4162):
|
||||
|
||||
```kotlin
|
||||
class ArchiveConfig(
|
||||
val archivePath: String, // Full path to the ZIP archive
|
||||
val disableCompression: Boolean?, // Skip compression for speed
|
||||
val parentTempDirectory: String? // Temp directory for extraction
|
||||
)
|
||||
```
|
||||
|
||||
### Export Flow
|
||||
|
||||
1. UI constructs an `ArchiveConfig` with a path under `databaseExportDir`.
|
||||
2. Calls `apiExportArchive(config)` which sends `CC.ApiExportArchive` to the Haskell core.
|
||||
3. The core creates a ZIP containing both `_chat.db` and `_agent.db` (and optionally files).
|
||||
4. Returns `CR.ArchiveExported` with a list of `ArchiveError` (non-fatal issues during export).
|
||||
5. UI offers the archive file for sharing/saving.
|
||||
|
||||
### Import Flow
|
||||
|
||||
1. User selects an archive file.
|
||||
2. UI copies it to a temp location and constructs an `ArchiveConfig`.
|
||||
3. Calls `apiImportArchive(config)` which sends `CC.ApiImportArchive` to the Haskell core.
|
||||
4. The core extracts and replaces both databases.
|
||||
5. Returns `CR.ArchiveImported` with a list of `ArchiveError` (non-fatal issues during import).
|
||||
6. UI triggers re-initialization via `initChatController`.
|
||||
|
||||
<a id="ArchiveError"></a>
|
||||
|
||||
### ArchiveError
|
||||
|
||||
Defined at [SimpleXAPI.kt#L7658](../common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt#L7658):
|
||||
|
||||
```kotlin
|
||||
sealed class ArchiveError {
|
||||
class ArchiveErrorImport(val importError: String) // General import error
|
||||
class ArchiveErrorFile(val file: String, val fileError: String) // Per-file error
|
||||
}
|
||||
```
|
||||
|
||||
### Delete Storage
|
||||
|
||||
`apiDeleteStorage()` removes both database files entirely. This is used during account deletion or database reset operations. After calling this, `initChatController` must be called to create fresh databases.
|
||||
|
||||
---
|
||||
|
||||
## 8. Source Files
|
||||
|
||||
| File | Purpose | Path |
|
||||
|------|---------|------|
|
||||
| SimpleXAPI.kt | API functions: encryption, export/import, storage commands | `common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt` |
|
||||
| Core.kt | JNI externals (`chatMigrateInit`, `chatEncryptFile`, etc.), `initChatController` | `common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt` |
|
||||
| Files.kt | Platform-expect file/directory path declarations | `common/src/commonMain/kotlin/chat/simplex/common/platform/Files.kt` |
|
||||
| Files.android.kt | Android actual paths (dataDir, appFilesDir, etc.) | `common/src/androidMain/kotlin/chat/simplex/common/platform/Files.android.kt` |
|
||||
| Files.desktop.kt | Desktop actual paths (XDG/AppData, etc.) | `common/src/desktopMain/kotlin/chat/simplex/common/platform/Files.desktop.kt` |
|
||||
| Cryptor.kt | Platform-expect encryption interface for passphrase storage | `common/src/commonMain/kotlin/chat/simplex/common/platform/Cryptor.kt` |
|
||||
| Cryptor.android.kt | Android: AES-GCM via AndroidKeyStore | `common/src/androidMain/kotlin/chat/simplex/common/platform/Cryptor.android.kt` |
|
||||
| Cryptor.desktop.kt | Desktop: placeholder (no-op) implementation | `common/src/desktopMain/kotlin/chat/simplex/common/platform/Cryptor.desktop.kt` |
|
||||
| DatabaseUtils.kt | `DBMigrationResult`, `MigrationError`, `MigrationConfirmation`, passphrase helpers | `common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DatabaseUtils.kt` |
|
||||
| Messages.hs | Haskell store: message CRUD, reactions, delivery | `src/Simplex/Chat/Store/Messages.hs` |
|
||||
| Groups.hs | Haskell store: groups, membership, roles | `src/Simplex/Chat/Store/Groups.hs` |
|
||||
| Direct.hs | Haskell store: contacts, direct connections | `src/Simplex/Chat/Store/Direct.hs` |
|
||||
| Files.hs | Haskell store: file transfer metadata | `src/Simplex/Chat/Store/Files.hs` |
|
||||
| Profiles.hs | Haskell store: user profiles | `src/Simplex/Chat/Store/Profiles.hs` |
|
||||
| Connections.hs | Haskell store: SMP agent connections | `src/Simplex/Chat/Store/Connections.hs` |
|
||||
|
||||
All Kotlin paths are relative to `apps/multiplatform/`. All Haskell paths are relative to the repository root.
|
||||
Reference in new issue
Block a user