config
The 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/.
Architecture
This package acts as the data layer for Matcha. It manages:
- Account configuration with multi-account support (Gmail, Outlook, iCloud, custom IMAP/SMTP)
- Secure credential storage via the OS keyring (with automatic migration from plain-text passwords), or inside the encrypted config when encryption is enabled
- Local caches for emails, email bodies, contacts, drafts, and folder listings to enable fast startup and offline browsing
- Email body caching for instant display of previously viewed emails without network round-trips
- Email signatures stored as plain text
- Optional encryption of all data files using AES-256-GCM with Argon2id key derivation
All 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.
Storage Layout
Configuration (~/.config/matcha/):
| Path | Description |
|---|---|
config.json |
Account settings, preferences |
signature.txt |
Email signature |
secure.meta |
Encryption metadata (salt + sentinel), only present when encryption is enabled |
pgp/ |
PGP keys |
plugins/ |
Installed Lua plugins |
themes/ |
Custom theme JSON files |
Cache (~/.cache/matcha/):
| Path | Description |
|---|---|
email_cache.json |
Email metadata cache |
contacts.json |
Contact autocomplete data |
drafts.json |
Saved email drafts |
folder_cache.json |
Folder listings per account |
folder_emails/ |
Per-folder email list cache |
email_bodies/ |
Cached email body content and attachment metadata |
On startup, MigrateCacheFiles() moves any cache files from the old location (~/.config/matcha/) to ~/.cache/matcha/.
Files
| File | Description |
|---|---|
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()). |
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). |
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. |
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. |
signature.go |
Loads and saves the user's email signature from ~/.config/matcha/signature.txt. |
oauth.go |
OAuth2 integration — token retrieval, authorization flow launcher, and embedded Python helper extraction. |
oauth_script.py |
Embedded OAuth2 helper script supporting Gmail and Outlook (browser-based auth, token refresh, secure storage). |
config_test.go |
Unit tests for configuration logic. |
Encryption
When enabled via Settings, all data files are encrypted at rest using a user-chosen password. The password is never stored anywhere.
How it works
- 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.
- Sentinel verification: A known phrase (
"matcha-verified") is encrypted with the derived key and stored insecure.meta. On login, if decrypting the sentinel succeeds, the password is correct. - 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 plainos.ReadFile/os.WriteFile. - Password storage: When encryption is active, account passwords are stored inside the encrypted
config.json(viasecureDiskAccount) instead of the OS keyring. When encryption is disabled, passwords are restored to the keyring.
secure.meta format
{
"salt": "<base64-encoded-32-byte-salt>",
"sentinel": "<base64-encoded-AES-GCM-encrypted-sentinel>",
"argon2_time": 3,
"argon2_memory": 65536,
"argon2_threads": 4
}
This file is never encrypted itself — its existence signals that encryption is enabled.
Email Body Cache
Email bodies and attachment metadata are cached per-folder in ~/.cache/matcha/email_bodies/. When a user views an email:
- The cache is checked first (
GetCachedEmailBody). - If found, the cached body is returned instantly without a network call.
- If not found, the body is fetched from the server and saved to cache (
SaveEmailBody). - When folder emails are refreshed, stale body cache entries (for emails no longer on the server) are pruned (
PruneEmailBodyCache).
Attachment binary data is not cached — only metadata (filename, MIME type, part ID, etc.) is stored. Attachment downloads always go to the server.
OAuth2 / XOAUTH2
Accounts with auth_method: "oauth2" use the XOAUTH2 mechanism instead of passwords. This is supported for Gmail and Outlook. The flow works across three layers:
-
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). TheIsOAuth2()method onAccountchecks the auth method. -
config/oauth_script.py— Embedded Python script that handles the full OAuth2 lifecycle for both Gmail and Outlook:auth— Opens a browser for authorization (Google or Microsoft), captures the callback onlocalhost: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.token— Returns a fresh access token, automatically refreshing if expired (with a 5-minute buffer).revoke— Revokes tokens and deletes local storage.- Client credentials are stored per provider:
~/.config/matcha/oauth_client.json(Gmail),~/.config/matcha/oauth_client_outlook.json(Outlook).
-
fetcher/xoauth2.go— Implements the XOAUTH2 SASL mechanism (sasl.Clientinterface) for IMAP/SMTP authentication. Formats the initial response asuser=<email>\x01auth=Bearer <token>\x01\x01per the XOAUTH2 protocol spec.