Configuration
Complete reference for ~/.config/mailypoppins/config.toml
Config File Location
All configuration lives in a single file: ~/.config/mailypoppins/config.toml.
Create it interactively with mp config init, or write it by hand.
View the resolved config at any time with mp config show.
Print the file path with mp config path.
Full Example
A complete single-account configuration with comments explaining each section.
theme = "mailypoppins" # TUI color theme (see Theme section)
notifications = false # Desktop notifications for new mail (see Notifications section)
[email]
font_family = "Helvetica, Arial, sans-serif" # HTML email font
font_size = "16px" # HTML email font size
include_signature = true # Append signature to outgoing mail
send_hold_secs = 20 # Undo-send window before hand-off (0 = send now)
[smtp]
host = "postout.lrz.de"
port = 587
username = "your-id"
default_from = "your.name@example.com"
# Password stored in OS keyring, not here
[imap]
host = "imap.example.com" # Falls back to smtp.host if omitted
port = 993
username = "" # Falls back to smtp.username if omitted
fetch_concurrency = 4 # Mailboxes fetched in parallel per sync (1-8)
body_fetch_deadline_secs = 30 # Per-mailbox download budget on a TUI sync (0 = unbounded)
sync_interval_secs = 900 # Daemon syncs after this long without a sync (0 = never)
# Password falls back to SMTP password if not set separately
[mailboxes.inbox]
server = "INBOX" # IMAP folder name; its messages are stored
# under the key "inbox" in the account database
[mailboxes.archive]
server = "Archive"
[mailboxes.sent]
server = "Sent Items"
[[mailboxes.extra]] # Additional mailboxes to sync
server = "Projects"
The file has no signature keys: each signature is a Markdown file under
~/.config/mailypoppins/signatures/. See Signatures.
Section Breakdown
theme
Selects the TUI color theme by name (helix-style: themes are built in and
picked in config, not defined in separate files). The key is top-level, so
it must appear before the first [section] header. Available themes:
mailypoppins: the default, used when the key is omitted; the Mac app's dark palette (aliases:mailypoppins-dark,default)mailypoppins-light: the Mac app's light palettecatppuccin-mocha: the TUI's original look (alias:catppuccin)catppuccin-latte: its light variant (alias:latte)tokyo-night(alias:tokyonight)terminal: adapts to the terminal's own ANSI palette: the background shows through and colors follow your terminal theme (aliases:transparent,ansi)
Names are case-insensitive. An unknown name does not fail: the TUI starts with the default theme and logs a warning to the activity log.
theme = "catppuccin-mocha" notifications
Opt-in desktop notifications for new mail while the TUI is running
(default: false). Like theme, the key is
top-level, so it must appear before the first [section]
header. When a background sync (including the automatic IDLE-triggered
fetches) saves new emails to the inbox, one notification is shown per
sync: sender: subject for a single new email, or
N new emails when several arrive at once. Read-status
updates, duplicate cleanup, and mailboxes other than the inbox never
trigger a notification.
Notifications use the platform's native tooling —
osascript on macOS and notify-send on Linux
— and degrade silently if the tool is unavailable.
notifications = true [email]
Controls the HTML rendering of sent emails. The font family and size are applied
to the generated HTML when Markdown is converted for sending. include_signature
determines whether the account signature is appended to a draft body on compose, reply, and forward.
send_hold_secs is the undo-send window: in the TUI a send is held for that many
seconds before it is handed to SMTP, and u cancels it in the meantime, leaving the
draft untouched. It defaults to 20 seconds; set it to 0 to send immediately.
[email]
font_family = "Helvetica, Arial, sans-serif"
font_size = "16px"
include_signature = true
send_hold_secs = 20 [smtp]
SMTP settings for sending email. The password is stored in the OS keyring
(macOS Keychain, Windows Credential Manager, or Linux Secret Service) under the
key smtp-password (or smtp-password-<account> for named accounts).
It is never written to the config file.
[smtp]
host = "postout.lrz.de"
port = 587
username = "your-id"
default_from = "your.name@example.com" [imap]
IMAP settings for fetching, syncing, and watching mailboxes. host,
port and username fall back to their SMTP counterparts
if omitted, so you only need to specify them when the IMAP server differs from SMTP.
fetch_concurrency is how many mailboxes a single sync fetches in
parallel, each on its own connection (default 4, clamped to the
range 1-8). A sync opens one connection per mailbox and overlaps their latency,
so more mailboxes finish in about the time of one. Lower it toward 1
if a server throttles concurrent connections.
body_fetch_deadline_secs is how long a single mailbox may spend
downloading message bodies on a sync started by the TUI (default 30,
maximum 600, 0 for no limit). A mailbox that runs out of
budget stops between batches, keeps what it has downloaded, and resumes on the next
sync, so a large backlog arrives over several syncs instead of holding one open;
newest mail is downloaded first. Nothing is deleted and no flag delta is advanced on
a sync that stopped early. mp sync ignores the budget and always
downloads everything it owes.
sync_interval_secs is how long the daemon lets the account go without
a sync before it runs a quick one on its own (default 900, fifteen
minutes; 0 turns it off; values under 60 are raised to
60). The daemon already syncs when new mail reaches the inbox; this
catches what that cannot see, such as changes in other folders, retries of queued
changes, and an account with no window open. Any sync, whether you started it or
the daemon did, restarts the wait. It does not apply to Microsoft Graph accounts
yet.
[imap]
host = "imap.example.com" # Falls back to smtp.host
port = 993
username = "" # Falls back to smtp.username
fetch_concurrency = 4 # Parallel mailbox fetches per sync (1-8)
body_fetch_deadline_secs = 30 # Per-mailbox body download budget, seconds (0 = unbounded)
sync_interval_secs = 900 # Seconds without a sync before the daemon runs one (0 = never) Data directory (not configurable)
Mail, drafts, contacts cache, OAuth2 tokens, and logs all live under a single OS-conventional app data directory:
- macOS:
~/Library/Application Support/mailypoppins/ - Linux (incl. WSL):
$XDG_DATA_HOME/mailypoppins/(default~/.local/share/mailypoppins/)
Layout under the data directory:
mailypoppins/
accounts/<name>/store.sqlite3 # synced mail: one row per message
accounts/<name>/blobs/ # message bodies and attachments, by hash
accounts/<name>/drafts/ # your drafts, one .md file each
accounts/<name>/attachments/<msg-id>/ # attachments a forward carries into a draft
accounts/<name>/contacts-cache.json # contacts index
tokens/<name>.enc # OAuth2 tokens
state.json # app state: each account's default signature
logs/mailypoppins-YYYY-MM-DD.log # daily logs, 200 MB at most in total mp config show prints the first three paths for every configured account.
Synced mail is not a file tree: a message is a row in store.sqlite3 plus
content-addressed blobs, and what you read in the TUI is rendered from those. Drafts are
the half that is still files, which is what you would symlink into an Obsidian vault:
accounts/<name>/drafts/. The location is not user-configurable per
account; override the entire data root with
MAILYPOPPINS_DATA_DIR=/some/path for tests or portable installs.
[mailboxes]
Three special-role mailboxes are required: inbox, archive,
and sent. Each has a server field (the IMAP folder name).
Synced mail lives in the account's store, keyed by the role name, which is also the
mailbox segment of an mp:// selector. The one directory left under the
account's data directory is drafts/.
Additional mailboxes can be added with the [[mailboxes.extra]] array
syntax. They are included in sync alongside the three core mailboxes, and their key is
the server name verbatim.
[mailboxes.inbox]
server = "INBOX"
[mailboxes.archive]
server = "Archive"
[mailboxes.sent]
server = "Sent Items"
[[mailboxes.extra]]
server = "Projects"
[[mailboxes.extra]]
server = "Newsletters" Signatures
A signature is one Markdown file in ~/.config/mailypoppins/signatures/.
The file name without its extension is the signature's name, so work.md
is the signature work and creating a signature is creating a file. The
account's default is recorded in the app's own state file
(<data_dir>/state.json), so config.toml carries nothing
about signatures at all.
~/.config/mailypoppins/signatures/
work.md
work-external.md
personal.md
Press cs in the TUI to manage them: the list shows every signature with
the default starred, Enter sets or clears the default,
e opens the selected file in $EDITOR, n creates
one, r renames, and d deletes after a confirmation.
mp config show prints the directory, the default, and a one-line preview
of each file.
HTML in a signature file (a rich signature exported from another mail client) is
converted to Markdown when it is read, links kept as [text](url), so the
draft you edit and both parts of the sent mail are always Markdown, never raw HTML.
The signature is appended to the draft body when you compose, reply, or forward,
so it is visible and editable in the draft rather than added on send. An account
with no default signature adds nothing. Use -s <name> to pick a
signature or --no-signature to skip it for one draft.
Signatures used to live in config.toml under
[accounts.signatures.*], inline as text or as a
path. The first run after the upgrade copies those entries out to files
and keeps the old default, then prints a notice. The tables are dead after that and
can be deleted by hand; mailypoppins never rewrites config.toml.
Two accounts could use the same entry name for different signatures, which the flat
directory has no room for, so the second one is copied to
<account>-<name>.md and that account's default points there.
The same name with the same content stays one file. If an entry cannot be copied,
because its name is not a usable file name or its path no longer reads,
the notice names it and stops telling you the tables can be deleted: they still hold
the only copy.
Multi-Account Setup
To manage multiple email accounts, use the [[accounts]] array syntax.
Each account has its own smtp, imap, and mailboxes
sections nested under it. The account name is used to identify it in
commands, keyring keys, and the data-dir layout (accounts/<name>/).
In the TUI, press ga to switch between accounts.
Two-account example
[email]
font_family = "Helvetica, Arial, sans-serif"
font_size = "16px"
include_signature = true
# --- First account ---
[[accounts]]
name = "personal"
default_from = "me@example.com"
auth_method = "password"
[accounts.smtp]
host = "smtp.example.com"
port = 587
username = "me@example.com"
[accounts.imap]
host = "imap.example.com"
port = 993
[accounts.mailboxes.inbox]
server = "INBOX"
[accounts.mailboxes.archive]
server = "Archive"
[accounts.mailboxes.sent]
server = "Sent"
# --- Second account ---
[[accounts]]
name = "work"
default_from = "name@company.com"
auth_method = "oauth2"
[accounts.oauth2]
client_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
tenant_id = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
[accounts.smtp]
host = "smtp.office365.com"
port = 587
username = "name@company.com"
[accounts.imap]
host = "outlook.office365.com"
port = 993
username = "name@company.com"
[accounts.mailboxes.inbox]
server = "INBOX"
[accounts.mailboxes.archive]
server = "Archive"
[accounts.mailboxes.sent]
server = "Sent Items" Authentication Methods
Each account has an auth_method field that determines how
credentials are handled. Three methods are supported.
1. Password (default)
Passwords are stored in the OS keyring (macOS Keychain, Windows Credential Manager,
Linux Secret Service). They are never written to the config file. Keyring keys
follow the pattern smtp-password-<name> and
imap-password-<name>. If the IMAP password is not set, it falls
back to the SMTP password.
Set or update passwords with:
mp config set-password Password auth config
[[accounts]]
name = "personal"
default_from = "me@example.com"
auth_method = "password" # This is the default; can be omitted
[accounts.smtp]
host = "smtp.example.com"
port = 587
username = "me@example.com" 2. OAuth2 (IMAP/SMTP)
For Microsoft 365 / Exchange Online accounts that support IMAP and SMTP.
Requires an Azure Entra ID app registration with IMAP.AccessAsUser.All
and SMTP.Send delegated permissions. Tokens are cached at
~/.email-cli/tokens/<account>.json and refreshed automatically.
Access tokens last about 1 hour; refresh tokens expire after roughly 90 days of
inactivity.
Run the device code flow to authenticate:
mp config oauth2-login --account work OAuth2 (IMAP/SMTP) config
[[accounts]]
name = "work"
default_from = "name@company.com"
auth_method = "oauth2"
[accounts.oauth2]
client_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
tenant_id = "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
[accounts.smtp]
host = "smtp.office365.com"
port = 587
username = "name@company.com"
[accounts.imap]
host = "outlook.office365.com"
port = 993
username = "name@company.com" 3. Graph API
For tenants that block IMAP and SMTP entirely. Uses the Microsoft Graph REST API
for all mail operations. No app registration of your own is needed: the client ID
of the shared mailypoppins app is built in, and the setup wizard
(mp config init or mp config add-account, option 4) uses it
when you press Enter at the client ID prompt. The tenant defaults to
common, which takes any work, school or personal Microsoft account;
you can still enter your tenant ID or domain instead.
The first sign-in asks for consent to the app's delegated permissions
(Mail.Read, Mail.ReadWrite, Mail.Send,
offline_access). Until the mailypoppins publisher is verified with Microsoft,
an administrator of your organisation has to approve the app at that first sign-in;
personal Microsoft accounts are not affected. An organisation that prefers its own app registration enters its client ID
at the prompt instead (see Azure App Registration below).
Graph accounts use well-known folder names (inbox,
archive, sentitems) instead of IMAP folder names.
SMTP and IMAP sections are not needed.
mp config oauth2-login --account exchange Graph API config
[[accounts]]
name = "exchange"
default_from = "name@company.com"
auth_method = "graph"
[accounts.oauth2]
client_id = "439d7ebd-7898-4df2-b2d6-88bb61e2597b" # the mailypoppins app
tenant_id = "common" # or your tenant ID or domain
[accounts.mailboxes.inbox]
server = "inbox" # Graph well-known folder name
[accounts.mailboxes.archive]
server = "archive"
[accounts.mailboxes.sent]
server = "sentitems" # Not "Sent Items" Azure App Registration
OAuth2 (IMAP/SMTP) authentication requires registering an application in Azure Entra ID (formerly Azure Active Directory). Graph accounts need one only when your organisation wants its own app instead of the mailypoppins app. Here is a condensed setup.
- Go to portal.azure.com and navigate to Microsoft Entra ID > App registrations > New registration.
- Name the app (e.g., "mailypoppins"), set supported account types to "Accounts in this organizational directory only", and leave the redirect URI blank.
- In the app's Authentication settings, scroll to Advanced settings and set "Allow public client flows" to Yes. This enables the device code flow.
-
Under API permissions, add delegated permissions:
-
For OAuth2 (IMAP/SMTP):
IMAP.AccessAsUser.AllandSMTP.Sendfrom Office 365 Exchange Online, plusoffline_accessfrom Microsoft Graph. -
For Graph API:
Mail.Read,Mail.ReadWrite, andMail.Sendfrom Microsoft Graph, plusoffline_access.
-
For OAuth2 (IMAP/SMTP):
- If permissions show "Needs admin consent", request your IT administrator to grant consent via the Azure portal.
-
From the app's Overview page, copy the Application (client) ID and
Directory (tenant) ID into your config's
[accounts.oauth2]section.
Keyring Management
Passwords for the password auth method are stored in the OS-native
keyring. The CLI uses the keyring Rust crate, which maps to:
- macOS: Keychain
- Windows: Credential Manager
- Linux: Secret Service (e.g., GNOME Keyring, KWallet)
Keyring keys follow this naming pattern:
smtp-password-<account-name>imap-password-<account-name>(falls back to SMTP password if not set)
To set or update a password:
mp config set-password The command prompts you to select which password to update (SMTP or IMAP) and which account. The password is entered interactively and never echoed to the terminal.