Website: https://www.ratmail.dev/
Read the docs: https://www.ratmail.dev/docs
Terminal email client with dual-channel viewing (rendered HTML + high-quality text) and ACL controlled cli for bots.
- Dual-channel viewing: rendered HTML + high-quality text
- First-ever rendered email HTML within the terminal (Kitty/Sixel)
- Fast text view with link/attachment overlays
- Send text and styled HTML emails
- IMAP sync with local cache (SQLite)
- Multiple accounts with per-account databases and tab switching
- Proton Mail support via Bridge (IMAP/SMTP)
- Rapid targetted search dialog
- Spellchecker (Hunspell via
spellbook, bundled en_US/en_GB) - Optional Vim-style modal composing
- Coding-agent-compatible CLI with JSON output, safety modes, and ACLs
- Commom preset and custom themes
Threat model:
- ACL enforcement is designed for agents running with normal user privileges.
- Do not grant root/admin access to agents if you want ACL protections to remain secure.
Strengths:
- CLI policy is signed and verified before use.
- Authoritative policy/state can be stored in root-owned system paths.
- Policy updates require an explicit privileged step (
sudo ratmail lock). - Onboarding defaults to locked-down AI access and explicit opt-in.
Limits:
- If an agent (or attacker) gets root/admin, local ACL protections can be bypassed.
- If a human approves privileged actions without review, unsafe changes can still be applied.
- This is local enforcement; it is not a substitute for server-side policy controls in high-risk environments.
- Install from GitHub:
cargo install --git https://github.com/peter-fm/ratmail.git --locked- Run:
ratmail-
On first run, Ratmail auto-creates:
~/.config/ratmail/ratmail.toml(or$XDG_CONFIG_HOME/ratmail/ratmail.toml) -
Edit that file with your account details (see Configuration below).
Use this when starting from a brand-new install.
- Run Ratmail setup:
ratmail setupExpected:
- Ratmail creates
~/.config/ratmail/ratmail.tomlautomatically. - Ratmail starts the setup walkthrough.
- Provider picker and form-based account setup in the splash screen.
- Optional multi-account add flow.
- Optional AI ACL setup with account scope selector (
one/many/all) and live permission summary. - Policy draft is written to
~/.config/ratmail/cli-policy.toml.
- Apply system policy (admin step):
sudo ratmail lockExpected:
- Root-owned signing keys are created automatically on first lock if missing.
- Ratmail shows a policy review (current vs new) and asks for confirmation.
- Signed policy is installed to system policy path.
- Monotonic policy version is recorded for rollback protection.
cargo run -p ratmailOr use the workspace alias:
cargo run-ratmailcargo install --git https://github.com/peter-fm/ratmail.git --lockedYou can publish prebuilt binaries to GitHub Releases with one command.
Prerequisites:
ghauthenticated (gh auth login)- Rust target(s) installed (the script auto-installs missing targets)
Run (single host target):
scripts/release-gh.sh v0.2.0Convenience wrappers:
scripts/macos-release.sh v0.2.0
scripts/linux-release.sh v0.2.0Run (multiple targets from one machine, if toolchain supports it):
scripts/release-gh.sh v0.2.0 --targets aarch64-apple-darwin,x86_64-apple-darwinIf you build on multiple machines, run the same tag on each machine; assets upload to the same release:
scripts/release-gh.sh v0.2.0 --appendOrder does not matter. The first run creates the release; later runs upload/update assets.
Outputs:
dist/v0.2.0/ratmail-0.2.0-<target>.tar.gzdist/v0.2.0/checksums.txt
Ratmail looks for ratmail.toml in the current directory first, then in
~/.config/ratmail/ratmail.toml (or $XDG_CONFIG_HOME/ratmail/ratmail.toml).
Relative db_path values are stored under ~/.local/state/ratmail
(or $XDG_STATE_HOME/ratmail).
Ratmail auto-creates ~/.config/ratmail/ratmail.toml on first run. Fill in one or more accounts:
[[accounts]]
name = "Personal"
# Relative paths are stored under ~/.local/state/ratmail
db_path = "ratmail-personal.db"
[accounts.imap]
host = "imap.example.com"
port = 993
username = "user@example.com"
password = "app-password-or-imap-password"
skip_tls_verify = false
initial_sync_days = 90
fetch_chunk_size = 10
[accounts.smtp]
host = "smtp.example.com"
port = 587
username = "user@example.com"
password = "app-password-or-smtp-password"
from = "Your Name <user@example.com>"
skip_tls_verify = false
[render]
remote_images = true
render_scale = 1.5
tile_height_px_side = 1000
tile_height_px_focus = 60
[ui]
folder_width_cols = 25
theme = "default"Notes:
- If
db_pathis omitted, it defaults toratmail-<account-name>.db. initial_sync_dayscontrols the first sync window; older mail can be loaded on demand.fetch_chunk_sizeis intentionally small for Proton Bridge reliability.- Render width is auto-calculated from terminal geometry and
render_scaleat runtime. ui.themecontrols the TUI palette (default,ratmail,nord,gruvbox,solarized-dark,solarized-light,dracula,catppuccin-mocha,catppuccin-latte,custom).ui.compose_vim = trueenables Vim-style modal editing in the compose body.- When
ui.theme = "custom", use[ui.palette]with hex colors (seeratmail.toml.example).
The / search supports field filters:
from:alicesubject:invoiceto:bobdate:2026-02-01since:2026-01-01before:2026-02-10- Attachments:
att:invoicefile:reporttype:pdfmime:image/png
Plain text without a field: prefix matches From/Subject/Preview.
- Tabs are shown in the top bar (e.g.,
1:Personal 2:Work). - Switch accounts with
[/]or by pressing a number key.
Tab/h/l: focus panesj/k: moveEnter: open messagev: toggle rendered/text viewp: toggle preview panes: sync selected folder (check for new mail)o: load older messages (backfill)?: toggle helpq: quit
Spellcheck uses Hunspell dictionaries via the spellbook crate.
Config options:
[spell]langselects the dictionary, e.g.en_USoren_GB.[spell]dirpoints to a directory containing<lang>.affand<lang>.dic.
Lookup order (first match wins):
RATMAIL_SPELL_DIR/<lang>.aff+RATMAIL_SPELL_DIR/<lang>.dicassets/dict/<lang>.aff+assets/dict/<lang>.dic/usr/share/hunspell/<lang>.*/usr/share/myspell/<lang>.*/usr/share/myspell/dicts/<lang>.*/usr/local/share/hunspell/<lang>.*/opt/homebrew/share/hunspell/<lang>.*/Library/Spelling/<lang>.*/System/Library/Spelling/<lang>.*
If no dictionary is found, spellcheck reports that it is unavailable.
Bundled dictionaries:
en_USanden_GBare embedded in the binary (and also kept underassets/dictwith their license files).
To install dictionaries:
-
Arch:
pacman -S hunspell-en_US hunspell-en_GB -
Debian/Ubuntu:
apt install hunspell-en-us hunspell-en-gb -
Fedora:
dnf install hunspell-en-US hunspell-en-GB -
macOS (Homebrew):
brew install hunspell(dictionaries are not bundled). Download Hunspell dictionaries separately and place.aff/.dicfiles in/usr/local/share/hunspell(Intel) or/opt/homebrew/share/hunspell(Apple Silicon). Example (Spanish,es_ES):- Download the LibreOffice dictionary extension for Spanish (
.oxt). - Extract
es_ES.affandes_ES.dicfrom the.oxt. - Copy them into your Hunspell dir.
- Set in
ratmail.toml:[spell] lang = "es_ES"
Concrete steps:
# Download the Spanish dictionary (.oxt) from LibreOffice Extensions curl -L -o es_ES.oxt "https://extensions.libreoffice.org/" # Extract and copy the hunspell files mkdir -p /tmp/ratmail-es && unzip -j es_ES.oxt "*/es_ES.aff" "*/es_ES.dic" -d /tmp/ratmail-es sudo mkdir -p /opt/homebrew/share/hunspell sudo cp /tmp/ratmail-es/es_ES.aff /opt/homebrew/share/hunspell/ sudo cp /tmp/ratmail-es/es_ES.dic /opt/homebrew/share/hunspell/
You can fetch LibreOffice dictionaries here (grab the
.oxtfor your language):https://extensions.libreoffice.org/ - Download the LibreOffice dictionary extension for Spanish (
Ratmail CLI returns JSON for scripting. It is off by default.
For stronger local security, CLI access rules now come from a signed policy blob in a system path (/etc/... on Linux, /Library/... on macOS), not from user-writable ACL fields.
Set this in ratmail.toml:
[cli]
enabled = true
default_account = "Personal"
[cli.policy]
# path = "/etc/ratmail/config.blob"
# state_path = "/var/lib/ratmail/cli-policy-state.json"
public_key_path = "/etc/ratmail/policy-public.pem"Simple flow for non-technical users:
- Run first-time setup:
ratmail setup - Apply as admin (interactive OS auth via sudo/polkit flow):
sudo ratmail lock
Setup behavior:
- CLI remains disabled unless you explicitly enable it.
- If enabled, defaults are AI-agent locked down.
- Each permission toggle explains what access you are granting.
For security assumptions and limits, see Security above.
Use these when changing policy later (without re-running full onboarding):
- Edit ACL interactively (account scope + permissions):
ratmail acl edit - Apply updated policy:
sudo ratmail lock
Power-user path (manual policy file):
- Edit
~/.config/ratmail/cli-policy.tomldirectly. - Optional custom signing (only if you maintain your own keypair):
ratmail acl sign --policy-toml ~/.config/ratmail/cli-policy.toml --secret-key /path/to/custom-private.pem --out config.blob - Apply:
sudo ratmail acl apply --policy config.blob
Examples:
ratmail accounts list
ratmail folders list --account Personal
ratmail messages list --account Personal --folder INBOX --limit 20
ratmail messages list --account Personal --folder INBOX --query "from:alice subject:invoice type:pdf"
ratmail message get --account Personal --id 123
ratmail message get --account Personal --id 123 --body --fetch
ratmail setup
ratmail acl edit
sudo ratmail lock
ratmail sync --account Personal --folder INBOX --wait --timeout-secs 60
ratmail send --account Personal --to alice@example.com --subject "Hi" --body "Test" --wait
ratmail message attachment-save --account Personal --id 123 --index 0 --path /tmp/invoice.pdf --fetchBridge uses a local IMAP/SMTP server with a self-signed cert. Use:
[accounts.imap]
host = "127.0.0.1"
port = 1143
skip_tls_verify = trueCommon gotchas:
- Use the Bridge-provided username/password (not your Proton password).
- Match the Bridge IMAP/SMTP ports and security mode (STARTTLS on 1143 is typical).
- If sync stalls, reduce
fetch_chunk_size(e.g., 5) and keepinitial_sync_dayssmall.
Configure each account with both IMAP and SMTP blocks:
[[accounts]]
name = "Personal"
db_path = "ratmail-personal.db"
[accounts.imap]
host = "imap.example.com"
port = 993
username = "you@example.com"
password = "imap-password-or-app-password"
skip_tls_verify = false
[accounts.smtp]
host = "smtp.example.com"
port = 587
username = "you@example.com"
password = "smtp-password-or-app-password"
from = "Your Name <you@example.com>"
skip_tls_verify = falseNotes:
fromis the sender label shown to recipients:Name <email@example.com>.- Many providers require app passwords (or provider-specific bridge passwords) instead of your normal login password.
The easiest way to use Gmail IMAP/SMTP is with an App Password (requires 2-Step Verification).
- Enable 2‑Step Verification on your Google account.
- Create an App Password for “Mail”.
- Edit
~/.config/ratmail/ratmail.tomland set both IMAP and SMTP:
[[accounts]]
name = "Gmail"
db_path = "ratmail-gmail.db"
[accounts.imap]
host = "imap.gmail.com"
port = 993
username = "you@gmail.com"
password = "your-16-char-app-password"
skip_tls_verify = false
[accounts.smtp]
host = "smtp.gmail.com"
port = 587
username = "you@gmail.com"
password = "your-16-char-app-password"
from = "Your Name <you@gmail.com>"
skip_tls_verify = falseNotes:
- Gmail requires an app password for IMAP/SMTP when 2‑Step Verification is enabled.
- OAuth (browser login) is possible but not implemented yet.

