DashMail

Technical Architecture & System Overview

How DashMail Works

Version 1.2  ·  May 2026  ·  Little Creek Door Co.  ·  Confidential

DashMail is a native Windows email client built on WinUI 3. It uses a local-first architecture — all email is stored in a SQLite database on the Windows host. The SMB network share is used only as a handoff zone: Mutt deposits incoming messages and DashMail queues outgoing ones.

Table of Contents

  1. 1.   Application Screenshots
  2. 2.   End-to-End System Architecture
  3. 3.   Email File Format
  4. 4.   Receiving Email — Step by Step
  5. 5.   Sending Email — Step by Step
  6. 6.   Local Storage (SQLite)
  7. 7.   SMB Share — Folder Structure
  8. 8.   Technology Stack
  9. 9.   Build Phases & Timeline
  10. 10. Backup & Restore

1. Application Screenshots

Current state of the DashMail prototype — built in React/JSX as a visual specification for the WinUI 3 native build.

Login Screen

DashMail Login Screen

Login screen — 50/50 split layout. Paper airplane logo + DashMail wordmark on dark left panel, mountain background photo on rounded right panel. Password-only authentication auto-matches account profile.

Main Application

DashMail Main Application

Main email client — three-pane layout: account/folder sidebar (left), email list with search (centre), reading pane with full email + attachments (right). Resizable panels, sign out button, connection status bar.

2. End-to-End System Architecture

DashMail follows the local-first (Option B) architecture. The SMB share acts only as a message handoff point — not a working mailstore.

Receiving Mail

Mail ServerIMAP/POP3
Mutt Daemonon server
SMB Shareinbox\
DashMail Poller60s / watcher
SQLite DBmail.db
DashMail UIWinUI 3

Sending Mail

DashMail UICompose
SQLite DBdrafts/sent
SMB Shareout\
Mutt Daemonsends via SMTP
Mail ServerSMTP
Core Principle
The SMB share is a handoff zone only. DashMail reads and writes exclusively to local SQLite during normal operation. A network outage never interrupts access to existing mail.

3. Email File Format

Each email on the SMB share is a folder containing these files. The parsing rules are deliberately simple:

.Mails\devon@littlecreekdoor.com\inbox\[message-folder]\ _headers_ <-- RFC 2822 headers (From, To, Subject, Date, Message-ID...) textfile0 <-- Plain text email body (only file rendered as message body) invoice.pdf <-- Any other files in the folder = attachments
FileRoleAction
_headers_RFC 2822 email headersParse: From, To, Subject, Date, Message-ID, Spam-Score
textfile0Email body — plain textStore as body_text in SQLite, render in reading pane
Everything elseAttachmentCopy to local attachments cache, register in DB
Header FieldExample ValueUI Usage
From:Charleen Roland <charroland@zoominternet.net>Sender name + avatar initials
Delivered-To:sales@littlecreekdoor.comAccount routing
Subject:Charleen and Rich RolandEmail list + reading pane title
Date:Sat, 23 May 2026 10:33:48 -0400Timestamp display
Message-ID:<1076592763...@zoominternet.net>Unique key / deduplication
X-Spam-Status:No, score=1.7Spam indicator in UI
Thread-Topic:Charleen and Rich RolandThread grouping

4. Receiving Email — Step by Step

  1. Mutt daemon polls the mail server (IMAP/POP3) on a schedule (~2 minutes).
  2. New messages are written as individual folders into \\mailserver\mail\.Mails\[account]\inbox\ containing _headers_, textfile0, and any attachments.
  3. DashMail Poller (background thread) detects new folders via FileSystemWatcher or 60-second timer.
  4. _headers_ is parsed with MimeKit HeaderList — From, To, Subject, Date, Message-ID, Spam-Score extracted.
  5. textfile0 is read as the email body. All other files in the folder are copied to the local attachment cache at %LOCALAPPDATA%\DashMail\attachments\[guid]\.
  6. A row is inserted into local mail.db. FTS5 full-text search index is updated automatically.
  7. The message folder is deleted from the SMB share — now fully owned by the local database.
  8. DashMail UI receives a live notification (ObservableCollection update) — new email appears instantly.
Offline Behaviour
If the SMB share is unreachable, steps 1–7 are skipped silently. The user has full access to all previously downloaded mail without a network connection.

5. Sending Email — Step by Step

  1. User composes in the Compose window. Draft is auto-saved to local SQLite continuously.
  2. User clicks Send. DashMail creates: \\mailserver\mail\.Mails\[account]\out\[timestamp]-[guid]\
  3. Writes _headers_: From, To, Subject, Date, Message-ID, X-Mailer: DashMail 1.0.
  4. Writes textfile0: the plain text message body.
  5. Copies any user attachments into the same folder.
  6. Email immediately moves to Sent folder in local SQLite — visible in Sent instantly.
  7. Mutt daemon detects the folder in out\, transmits via SMTP, removes the folder.

6. Local Storage — SQLite Schema

CREATE TABLE emails ( id TEXT PRIMARY KEY, -- from Message-ID header account TEXT, -- devon@littlecreekdoor.com folder TEXT, -- inbox | sent | drafts | archive | trash from_addr TEXT, -- "Charleen Roland <charroland@...>" to_addr TEXT, subject TEXT, body_text TEXT, -- contents of textfile0 date DATETIME, is_read INTEGER DEFAULT 0, is_starred INTEGER DEFAULT 0, spam_score REAL, -- parsed from X-Spam-Status score= has_attachments INTEGER DEFAULT 0, modified_at DATETIME DEFAULT (datetime('now')) -- updated by trigger on any change ); CREATE TRIGGER emails_update_modified AFTER UPDATE ON emails BEGIN UPDATE emails SET modified_at = datetime('now') WHERE id = NEW.id; END; CREATE INDEX idx_emails_modified ON emails(modified_at); -- Backup chain tracking CREATE TABLE backup_log ( id TEXT PRIMARY KEY, -- GUID type TEXT, -- 'full' | 'incremental' base_id TEXT, -- parent full backup GUID (incrementals only) created_at DATETIME, zip_path TEXT, -- absolute path to backup ZIP email_count INTEGER, size_bytes INTEGER, checksum TEXT -- SHA-256 of ZIP file ); CREATE TABLE attachments ( id TEXT PRIMARY KEY, email_id TEXT, filename TEXT, -- "invoice.pdf", "photo.jpg", etc. local_path TEXT, size_bytes INTEGER ); CREATE VIRTUAL TABLE email_fts USING fts5(subject, body_text, from_addr);

7. SMB Share — Folder Structure

⚠ Critical constraint — Read-only share policy:
DashMail never deletes, moves, renames, or modifies any file or folder on the SMB share. The share is accessed concurrently by multiple users and Mutt is solely responsible for managing the inbox and sent folders. DashMail uses Message-ID deduplication in SQLite to avoid re-importing messages it has already seen — no folder is cleared or touched after reading.
FolderManaged byDashMail action
inbox\Mutt (writes incoming message folders)Reads _headers_ + textfile0, checks Message-ID against SQLite, imports unseen — never deletes
out\DashMail (writes outgoing message folders)Writes new _headers_ + textfile0 folder; Mutt picks up and sends
sent\Mutt (optional mirror)Not modified by DashMail
drafts\Not used on SMB; drafts live in local SQLite only
archive\Not used on SMB; archive lives in local SQLite only
trash\Not used on SMB; deleted emails are flagged locally only
\\mailserver\mail\.Mails\ devon@littlecreekdoor.com\ inbox\ <-- Mutt writes here; DashMail reads & never deletes out\ <-- DashMail writes here; Mutt sends & clears sent\ <-- Mutt mirror after sending (read-only for DashMail) drafts\ <-- Not used (drafts are local SQLite only) archive\ <-- Not used (archive is local SQLite only) trash\ <-- Not used (trash is local SQLite only) sales@littlecreekdoor.com\ inbox\ out\ sent\ ... dmbont23@mailbox.org\ inbox\ out\ sent\ ... default\ inbox\ out\ sent\ ...

8. Technology Stack

LayerTechnologyPurpose
UI FrameworkWinUI 3 / Windows App SDK 1.6+Native Windows controls, Fluent Design, Mica material
LanguageC# 12All app logic, services, ViewModels
UI PatternMVVM (CommunityToolkit.Mvvm)Data binding, commands, ObservableCollection
Header ParsingMimeKit / MailKitParse RFC 2822 _headers_ files, write outgoing headers
Email BodyPlain text (textfile0)Read as-is; write plain text for outgoing mail
Local DatabaseSQLite + FTS5 (Microsoft.Data.Sqlite)All email storage + instant full-text search
SMB AccessNative Windows UNC pathsRead inbox\, write out\ — no extra library needed
File WatchingFileSystemWatcher (.NET)Real-time detection of new message folders on SMB
Reading PaneRichTextBlock / WebView2Render textfile0 body
NotificationsAppNotificationBuilderWindows Action Center toasts for new mail
PackagingMSIXInstaller, auto-update, Windows Store compatible
IDEVisual Studio 2022Build, XAML designer, debugger

Key NuGet Packages

Microsoft.WindowsAppSDK // WinUI 3 framework CommunityToolkit.Mvvm // ObservableObject, RelayCommand CommunityToolkit.WinUI.UI // Additional WinUI controls MimeKit // RFC 2822 _headers_ parsing and writing Microsoft.Data.Sqlite // SQLite + FTS5 full-text search WinUIEx // Extended window management

9. Build Phases & Timeline

1
Project Shell — VS 2022, custom title bar, Mica, login page (logo, password-only auth, mountain photo) · 2 days
2
Main Email Shell — sidebar, email ListView, reading pane, GridSplitter resize, status bar, sign out · 3 days
3
SMB + Data Layer — SmbService, MailService, SQLite schema, FileSystemWatcher, _headers_ parser, textfile0 reader, attachment cache · 2 days
4
Compose & Send — Compose window, reply/forward, attachments, write _headers_ + textfile0 to SMB out\ · 2 days
5
Settings — General, Appearance, Notifications, Accounts, Filters, Privacy, Encryption, Spam, Backup, Advanced · 3 days
6
Polish & Features — context menus, attachment viewer/download/print, notifications, dark/light theme, keyboard shortcuts · 2 days
7
Packaging — MSIX installer, app icon, version info, auto-update manifest · 1 day
Total estimated build time:  2 – 3 weeks

10. Backup & Restore

Overview

DashMail stores all email, settings, and metadata in a local SQLite database (mail.db). The SMB share is never modified during backup or restore. Users can schedule automatic backups, choose between Smart / Always Full / Always Incremental modes, configure multiple backup destinations, and restore from any point in the backup chain.

Backup Format

Each backup is a timestamped ZIP archive containing:

dashmail-backup-20260524-220001-full.zip manifest.json // type, created_at, base_id, email_count, checksum mail.db // complete SQLite database (via online backup API) settings.json // profile + app settings snapshot attachments\ // all cached attachment files (full backup only) dashmail-backup-20260525-220001-inc.zip manifest.json // type:"incremental", base_id:"<full GUID>" email_changes.sql // UPSERT statements for rows where modified_at > last backup deleted_ids.json // locally-deleted email IDs (SMB share is unchanged) attachments\ // only attachments added since last backup

Backup Modes

ModeBehaviourStorage impact
Smart (recommended)Daily incrementals; auto-resets to full every 30 days or 10 incrementalsLow — incremental ZIPs are typically < 2 MB
Always fullEvery scheduled run produces a complete backupHigh — each run copies entire database
Always incrementalOnly changes since last backup are capturedVery low — requires a full backup to exist first

Incremental Change Detection

The emails.modified_at column is updated automatically by a SQLite trigger on every UPDATE (read/unread, starred, moved, label changes). At backup time, DashMail queries SELECT * FROM emails WHERE modified_at > :last_backup_ts and serialises the results as INSERT OR REPLACE statements into email_changes.sql.

-- Trigger that keeps modified_at current on any row change CREATE TRIGGER emails_update_modified AFTER UPDATE ON emails BEGIN UPDATE emails SET modified_at = datetime('now') WHERE id = NEW.id; END;

Backup Chain Management

A new full backup starts a chain. Its GUID becomes the base_id for all subsequent incrementals.
Each incremental records base_id = <full GUID> in its manifest so restore knows the required base.
After 30 days or 10 incrementals, Smart mode automatically triggers a fresh full backup, starting a new chain.
When the retention limit is reached (default: 5 sets), the oldest complete chain (full + all its incrementals) is deleted from the backup folder.
Scheduled backups are registered with Windows Task Scheduler at setup time. DashMail registers a task that runs even when the app is closed.

Restore Procedure

Safety backup — DashMail automatically creates a full backup of the current state before any restore begins.
Select point — User picks a full or incremental backup from the history list (or browses to a ZIP file).
Base restore — The full backup in the chain is applied first: mail.db replaces the live database via SQLite online backup API.
Incremental replay — Each email_changes.sql in the chain is executed in date order up to the selected restore point.
Attachment merge — Cached attachment files are restored to the local cache folder. The SMB share is never touched.
Restart required — DashMail prompts the user to restart so all services reload from the restored database.
Key guarantee: The SMB share is never read, written to, or modified during backup or restore. Backup operates entirely on the local SQLite database and attachment cache. Multiple users can continue accessing the share without interruption.