1# config
2
3The `config` package handles all persistent application state: user configuration, email/contacts/drafts caching, folder caching, email signatures, and optional at-rest encryption. Configuration is stored under `~/.config/matcha/`, cache data under `~/.cache/matcha/`.
4
5## Architecture
6
7This package acts as the data layer for Matcha. It manages:
8
9- **Account configuration** with multi-account support (Gmail, Outlook, iCloud, custom IMAP/SMTP)
10- **Secure credential storage** via the OS keyring (with automatic migration from plain-text passwords), or inside the encrypted config when encryption is enabled
11- **Local caches** for emails, email bodies, contacts, drafts, and folder listings to enable fast startup and offline browsing
12- **Email body caching** for instant display of previously viewed emails without network round-trips
13- **Email signatures** stored as plain text
14- **Optional encryption** of all data files using AES-256-GCM with Argon2id key derivation
15
16All files use JSON serialization with restrictive file permissions (`0600`/`0700`). When encryption is enabled, all files (except `secure.meta`) are encrypted before writing to disk.
17
18## Storage Layout
19
20Configuration (`~/.config/matcha/`):
21
22| Path | Description |
23|------|-------------|
24| `config.json` | Account settings, preferences |
25| `signature.txt` | Email signature |
26| `secure.meta` | Encryption metadata (salt + sentinel), only present when encryption is enabled |
27| `pgp/` | PGP keys |
28| `plugins/` | Installed Lua plugins |
29| `themes/` | Custom theme JSON files |
30
31Cache (`~/.cache/matcha/`):
32
33| Path | Description |
34|------|-------------|
35| `email_cache.json` | Email metadata cache |
36| `contacts.json` | Contact autocomplete data |
37| `drafts.json` | Saved email drafts |
38| `folder_cache.json` | Folder listings per account |
39| `folder_emails/` | Per-folder email list cache |
40| `email_bodies/` | Cached email body content and attachment metadata |
41
42On startup, `MigrateCacheFiles()` moves any cache files from the old location (`~/.config/matcha/`) to `~/.cache/matcha/`.
43
44## Files
45
46| File | Description |
47|------|-------------|
48| `config.go` | Core configuration types (`Account`, `Config`, `MailingList`) and functions for loading, saving, and managing accounts. Handles IMAP/SMTP server resolution per provider, OS keyring integration, legacy config migration, and cache directory management (`cacheDir()`, `MigrateCacheFiles()`). |
49| `cache.go` | Email, contacts, drafts, and email body caching. Provides CRUD operations for `EmailCache`, `ContactsCache` (with search and frequency-based ranking), `DraftsCache` (with save/delete/get operations), and `EmailBodyCache` (per-folder body + attachment metadata caching with pruning). |
50| `folder_cache.go` | Caches IMAP folder listings per account and per-folder email metadata. Stores folder names to avoid repeated IMAP `LIST` commands, and caches email headers per folder for fast navigation. |
51| `encryption.go` | Optional at-rest encryption using AES-256-GCM with Argon2id key derivation. Provides `SecureReadFile`/`SecureWriteFile` (transparent encryption wrappers used by all other files), `EnableSecureMode`/`DisableSecureMode`, password verification via an encrypted sentinel phrase, and session key management. |
52| `signature.go` | Loads and saves the user's email signature from `~/.config/matcha/signature.txt`. |
53| `oauth.go` | OAuth2 integration — token retrieval, authorization flow launcher, and embedded Python helper extraction. |
54| `oauth_script.py` | Embedded OAuth2 helper script supporting Gmail and Outlook (browser-based auth, token refresh, secure storage). |
55| `config_test.go` | Unit tests for configuration logic. |
56
57## Encryption
58
59When enabled via Settings, all data files are encrypted at rest using a user-chosen password. The password is never stored anywhere.
60
61### How it works
62
631. **Key derivation**: The password is combined with a random 256-bit salt using Argon2id (time=3, memory=64MB, threads=4) to produce a 256-bit AES key.
642. **Sentinel verification**: A known phrase (`"matcha-verified"`) is encrypted with the derived key and stored in `secure.meta`. On login, if decrypting the sentinel succeeds, the password is correct.
653. **Transparent I/O**: All file read/write operations go through `SecureReadFile`/`SecureWriteFile`. When a session key is set, these encrypt/decrypt automatically. When no key is set (encryption disabled), they pass through to plain `os.ReadFile`/`os.WriteFile`.
664. **Password storage**: When encryption is active, account passwords are stored inside the encrypted `config.json` (via `secureDiskAccount`) instead of the OS keyring. When encryption is disabled, passwords are restored to the keyring.
67
68### `secure.meta` format
69
70```json
71{
72 "salt": "<base64-encoded-32-byte-salt>",
73 "sentinel": "<base64-encoded-AES-GCM-encrypted-sentinel>",
74 "argon2_time": 3,
75 "argon2_memory": 65536,
76 "argon2_threads": 4
77}
78```
79
80This file is never encrypted itself — its existence signals that encryption is enabled.
81
82## Email Body Cache
83
84Email bodies and attachment metadata are cached per-folder in `~/.cache/matcha/email_bodies/`. When a user views an email:
85
861. The cache is checked first (`GetCachedEmailBody`).
872. If found, the cached body is returned instantly without a network call.
883. If not found, the body is fetched from the server and saved to cache (`SaveEmailBody`).
894. When folder emails are refreshed, stale body cache entries (for emails no longer on the server) are pruned (`PruneEmailBodyCache`).
90
91Attachment binary data is not cached — only metadata (filename, MIME type, part ID, etc.) is stored. Attachment downloads always go to the server.
92
93## OAuth2 / XOAUTH2
94
95Accounts with `auth_method: "oauth2"` use the XOAUTH2 mechanism instead of passwords. This is supported for Gmail and Outlook. The flow works across three layers:
96
971. **`config/oauth.go`** — Go-side orchestration. Extracts the embedded Python helper to `~/.config/matcha/oauth/`, invokes it to run the browser-based authorization flow (`RunOAuth2Flow`) or to retrieve a fresh access token (`GetOAuth2Token`). The `IsOAuth2()` method on `Account` checks the auth method.
98
992. **`config/oauth_script.py`** — Embedded Python script that handles the full OAuth2 lifecycle for both Gmail and Outlook:
100 - `auth` — Opens a browser for authorization (Google or Microsoft), captures the callback on `localhost:8189`, exchanges the code for tokens, and saves them to `~/.config/matcha/oauth_tokens/`. The provider is auto-detected from the email domain or can be specified with `--provider`.
101 - `token` — Returns a fresh access token, automatically refreshing if expired (with a 5-minute buffer).
102 - `revoke` — Revokes tokens and deletes local storage.
103 - Client credentials are stored per provider: `~/.config/matcha/oauth_client.json` (Gmail), `~/.config/matcha/oauth_client_outlook.json` (Outlook).
104
1053. **`fetcher/xoauth2.go`** — Implements the XOAUTH2 SASL mechanism (`sasl.Client` interface) for IMAP/SMTP authentication. Formats the initial response as `user=<email>\x01auth=Bearer <token>\x01\x01` per the XOAUTH2 protocol spec.