This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Mailspring-Sync is the native C++11 sync engine for the Mailspring email client. It handles email, contact, and calendar synchronization via IMAP/SMTP using MailCore2, storing data in SQLite with a JSON-based schema.
# Install dependencies (Ubuntu)
sudo apt install libc-ares-dev libicu-dev libctemplate-dev libtidy-dev uuid-dev libxml2-dev libsasl2-dev liblzma-dev libcurl4-openssl-dev libglib2.0-dev libssl-dev
# Build (from project root)
cd Vendor/libetpan && ./autogen.sh && make && sudo make install prefix=/usr
cd Vendor/mailcore2 && mkdir -p build && cd build && cmake .. && make
cmake . && makexcodebuild -scheme mailsync -configuration ReleaseWindows builds use vcpkg for dependency management. Dependencies are defined in vcpkg.json at the project root.
Local Development:
# Install vcpkg (one-time setup)
git clone https://github.com/microsoft/vcpkg.git C:\vcpkg
C:\vcpkg\bootstrap-vcpkg.bat
set VCPKG_ROOT=C:\vcpkg
# Install dependencies (from project root)
vcpkg install --triplet x86-windows
# Build
cd Windows
msbuild.exe mailsync.sln /property:Configuration=Release;Platform=Win32CI/CD: GitHub Actions automatically installs vcpkg dependencies during the build (see .github/workflows/build-windows.yml).
vcpkg-managed dependencies: openssl, curl, libxml2, zlib, icu, libiconv, tidy-html5, ctemplate, pthreads, cyrus-sasl
Requires environment variables and command-line arguments:
CONFIG_DIR_PATH=/path/to/config IDENTITY_SERVER=https://id.getmailspring.com \
./mailsync --identity "<identity-json>" --account "<account-json>" --mode syncModes: sync, test (auth validation), reset (clear account data), migrate (run migrations)
For debugging in Xcode/Visual Studio, configure the debugger to pass --identity, --account, and --mode arguments.
All database changes flow through an entity layer and are emitted as a JSON event stream to stdout, enabling the Mailspring UI to reactively update.
Data Flow:
- Model modification → Caller modifies a
MailModelsubclass (Message, Thread, Folder, etc.) - Save via MailStore →
store->save(model)increments version, callsbeforeSave(), writes to SQLite, callsafterSave() - Delta creation → Save/remove creates a
DeltaStreamItemwith type "persist" or "unpersist" - Delta emission →
_emit()queues the delta (immediately or within transaction) - Output to stdout →
DeltaStreamflushes buffered deltas as newline-separated JSON
Transaction Behavior:
MailStoreTransaction transaction(store, "operationName");
store->save(model1); // Delta accumulated
store->save(model2); // Delta accumulated
transaction.commit(); // All deltas emitted together
// Destructor auto-rollbacks if commit() not calledDelta Coalescing: Multiple saves of the same object within a flush window are merged—only the final state is emitted, with keys merged to preserve conditionally-included fields (e.g., message.body).
Output Format:
{"type":"persist","modelClass":"Message","modelJSONs":[{...}]}
{"type":"unpersist","modelClass":"Thread","modelJSONs":[{"id":"..."}]}Key Classes:
MailModel(MailSync/Models/MailModel.hpp) - Base class with_dataJSON, lifecycle hooks, version trackingMailStore(MailSync/MailStore.hpp) - Database layer, emits deltas on save/removeMailStoreTransaction- RAII transaction wrapper, batches deltas until commitDeltaStream(MailSync/DeltaStream.hpp) - Singleton (SharedDeltaStream()) managing stdout output with buffering
Each mailsync process handles a single email account. Mailspring runs one process per connected account. The process communicates via:
- stdout: Emits newline-separated JSON for all model changes (see Reactive Data Flow above)
- stdin: Accepts JSON task objects and commands (queue-task, cancel-task, wake-workers, need-bodies)
- Main thread: Listens on stdin for tasks and commands
- Background thread (
SyncWorker): Iterates folders, performs incremental sync using CONDSTORE/XYZRESYNC - Foreground thread (
SyncWorker): IDLEs on primary folder, handles body fetches and task execution - CalContacts thread (
DAVWorker,GoogleContactsWorker): Calendar/contact sync via CardDAV/CalDAV - Metadata threads (
MetadataWorker,MetadataExpirationWorker): Syncs plugin metadata to/from id.getmailspring.com
MailStore: SQLite database wrapper with template-based queries. Uses "fat" rows with adataJSON column plus indexed columns for queryable fields. See Reactive Data Flow above.TaskProcessor: Handles local (immediate) and remote (network) task execution for operations like sending mail, modifying flags, etc.MailProcessor: Parses IMAP messages, creates stable IDs from headers
Account, Message, Thread, Folder, Label, Contact, ContactBook, ContactGroup, Calendar, Event, File, Task, Identity
Located in Vendor/ - these are built from source and some contain local modifications:
- libetpan - IMAP/SMTP library (modified from upstream)
- mailcore2 - High-level mail library (modified from upstream)
- SQLiteCpp - SQLite C++ wrapper
- nlohmann-json - JSON library (header-only)
- spdlog - Logging library
- icalendarlib - iCalendar parsing
- StanfordCPPLib - Utility library
On Windows, external binary dependencies (OpenSSL, curl, libxml2, etc.) are managed via vcpkg rather than vendored binaries.
Gmail accounts sync only Spam, All Mail, and Trash folders, using X-GM-LABELS extension for label handling. Virtual folders are ignored.
New source files must be manually added to:
CMakeLists.txt(Linux)MailSync.xcodeproj(macOS)Windows/mailsync.vcxproj(Windows)