Install
Download the latest release binary for your platform from the GitHub releases page.
# macOS (Apple Silicon)
curl -LO https://github.com/peter-fm/ratmail/releases/download/v0.2.4/ratmail-v0.2.4-macos-arm64
chmod +x ratmail-v0.2.4-macos-arm64
sudo mv ratmail-v0.2.4-macos-arm64 /usr/local/bin/ratmail
# Linux (x86_64)
curl -LO https://github.com/peter-fm/ratmail/releases/download/v0.2.4/ratmail-v0.2.4-linux-x86_64
chmod +x ratmail-v0.2.4-linux-x86_64
sudo mv ratmail-v0.2.4-linux-x86_64 /usr/local/bin/ratmail Alternatively, install from source with Cargo:
cargo install --git https://github.com/peter-fm/ratmail.git --locked After install, verify your binary is reachable in $PATH and check command help if needed:
ratmail --help Run:
ratmail First run auto-creates:
~/.config/ratmail/ratmail.toml(or$XDG_CONFIG_HOME/ratmail/ratmail.toml)~/.config/ratmail/.setup-completeafter setup completion
Keep ratmail.toml private. It can contain IMAP/SMTP credentials and should not be committed
to source control.
Run from source in the app repo:
cargo run -p ratmail From-source runs are useful when iterating on Ratmail internals, trying unreleased behavior, or validating changes to local CLI workflows before publishing docs.
macOS App
You can wrap Ratmail in a standard .app bundle so it appears in your Dock, Spotlight,
and Launchpad like a native app. The bundle launches your preferred terminal with Ratmail running inside it.
1. Create the bundle structure
mkdir -p /Applications/Ratmail.app/Contents/MacOS
mkdir -p /Applications/Ratmail.app/Contents/Resources 2. Add the Info.plist
Create /Applications/Ratmail.app/Contents/Info.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>ratmail</string>
<key>CFBundleIdentifier</key>
<string>com.local.ratmail</string>
<key>CFBundleExecutable</key>
<string>ratmail</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleIconFile</key>
<string>ratmail</string>
<key>LSUIElement</key>
<false/>
</dict>
</plist> 3. Add the launcher script
Create /Applications/Ratmail.app/Contents/MacOS/ratmail and chmod +x it. Pick the version matching your terminal below.
Ghostty
Ghostty supports a config file with an initial command, so the launcher creates a temporary config and passes it directly.
First, create /Applications/Ratmail.app/Contents/Resources/ghostty-ratmail.conf:
window-save-state = never
initial-command = direct:{APP_RATMAIL_PATH}
quit-after-last-window-closed = true
window-height = 37
window-width = 159 Then the launcher script:
#!/bin/zsh
set -euo pipefail
APP_DIR="$(cd "$(dirname "$0")/.." && pwd)"
CFG_SRC="$APP_DIR/Resources/ghostty-ratmail.conf"
export PATH="$HOME/.cargo/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
if command -v ratmail >/dev/null 2>&1; then
TUI_BIN="$(command -v ratmail)"
else
TUI_BIN="$APP_DIR/Resources/ratmail"
fi
CFG_TMP="$(mktemp -t ghostty-ratmail-config.XXXXXX)"
sed "s|{APP_RATMAIL_PATH}|$TUI_BIN|g" "$CFG_SRC" > "$CFG_TMP"
exec /Applications/Ghostty.app/Contents/MacOS/ghostty \
--config-default-files=false \
--config-file="$CFG_TMP" Kitty
#!/bin/zsh
set -euo pipefail
export PATH="$HOME/.cargo/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
if command -v ratmail >/dev/null 2>&1; then
TUI_BIN="$(command -v ratmail)"
else
TUI_BIN="$(cd "$(dirname "$0")/.." && pwd)/Resources/ratmail"
fi
exec /Applications/kitty.app/Contents/MacOS/kitty \
--single-instance \
--title Ratmail \
"$TUI_BIN" iTerm2
#!/bin/zsh
set -euo pipefail
export PATH="$HOME/.cargo/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
if command -v ratmail >/dev/null 2>&1; then
TUI_BIN="$(command -v ratmail)"
else
TUI_BIN="$(cd "$(dirname "$0")/.." && pwd)/Resources/ratmail"
fi
osascript -e "
tell application \"iTerm\"
create window with default profile command \"$TUI_BIN\"
end tell" Alacritty
#!/bin/zsh
set -euo pipefail
export PATH="$HOME/.cargo/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
if command -v ratmail >/dev/null 2>&1; then
TUI_BIN="$(command -v ratmail)"
else
TUI_BIN="$(cd "$(dirname "$0")/.." && pwd)/Resources/ratmail"
fi
exec /Applications/Alacritty.app/Contents/MacOS/alacritty \
--title Ratmail \
-e "$TUI_BIN" Terminal.app
#!/bin/zsh
set -euo pipefail
export PATH="$HOME/.cargo/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
if command -v ratmail >/dev/null 2>&1; then
TUI_BIN="$(command -v ratmail)"
else
TUI_BIN="$(cd "$(dirname "$0")/.." && pwd)/Resources/ratmail"
fi
osascript -e "
tell application \"Terminal\"
do script \"$TUI_BIN\"
activate
end tell" 4. Add an icon (optional)
Place a ratmail.icns file in the Resources directory. You can create one from a PNG using:
mkdir ratmail.iconset
sips -z 512 512 icon.png --out ratmail.iconset/icon_256x256@2x.png
sips -z 256 256 icon.png --out ratmail.iconset/icon_256x256.png
sips -z 128 128 icon.png --out ratmail.iconset/icon_128x128.png
# ... add more sizes as desired
iconutil -c icns ratmail.iconset -o /Applications/Ratmail.app/Contents/Resources/ratmail.icns 5. Register with the system
Touch the app so macOS picks up the bundle:
touch /Applications/Ratmail.app Ratmail should now appear in Spotlight and Launchpad. You can also drag it onto your Dock.
Notes
- The launcher looks for
ratmailon yourPATHfirst, falling back to a binary placed inContents/Resources/ratmail. - Rendered HTML mode requires a terminal with Kitty or Sixel image protocol support. Terminal.app does not support image protocols, so it will fall back to text mode.
- Adjust
window-heightandwindow-widthin the Ghostty config (or equivalent flags for other terminals) to suit your display.
Onboarding Setup
The setup wizard is the fastest way to get a valid account config in one pass. It is designed for first-run and for incrementally adding additional accounts later.
Run interactive setup:
ratmail setup The wizard flow includes:
- Provider pick: Gmail, Proton Bridge, Yahoo Mail, or custom IMAP/SMTP
- Account form (name, email/username, password, display name, provider hosts/ports)
- Optional multi-account add
- Optional AI CLI enablement
Generated defaults for each account:
db_path = "ratmail-<slug>.db"imap.initial_sync_days = 90imap.fetch_chunk_size = 10
If AI CLI is enabled, setup writes:
[cli]defaults intoratmail.toml
Setup keeps CLI onboarding minimal. Enable it when needed, then use standard Ratmail subcommands directly.
Config Reference
Ratmail merges behavior from TOML config plus runtime defaults. If a key is omitted, Ratmail falls back to built-in safe values for that section.
Ratmail checks config paths in this order:
./ratmail.toml$XDG_CONFIG_HOME/ratmail/ratmail.toml(or~/.config/ratmail/ratmail.toml)
Accounts
Each [[accounts]] entry is an independently addressable mailbox with its own database file.
Multi-account tabs in the TUI map directly to this list.
[[accounts]]
name = "Personal"
db_path = "ratmail-personal.db"
[accounts.imap]
host = "imap.example.com"
port = 993
username = "user@example.com"
password = "app-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"
from = "Your Name <user@example.com>"
skip_tls_verify = false Notes:
- Relative
db_pathresolves under$XDG_STATE_HOME/ratmail(or~/.local/state/ratmail) - If
db_pathis omitted, default isratmail-<slug-account-name>.db imap.fetch_chunk_sizeis clamped to1..50
Operational guidance: keep initial_sync_days moderate for first sync speed, then use backfill
from the TUI (o) as needed for older history.
Render
Render settings control HTML-to-terminal image behavior. In rendered mode, Ratmail chooses geometry based on terminal dimensions and these values.
render.remote_images: bool, defaulttruerender.width_px: default800(runtime geometry can override)render.render_scale: default1.5, clamped0.25..4.0render.tile_height_px_side: default1000render.tile_height_px_focus: default60
If your terminal does not support image protocols (Kitty/Sixel), Ratmail still works in text mode and falls back gracefully.
Send
Send settings affect outgoing message formatting and generated HTML alternatives when sending from compose or CLI.
send.html: defaulttruesend.font_family: defaultArial, sans-serifsend.font_size_px: default14, clamped8..72
If you want plainest output for compatibility-sensitive recipients, set send.html = false.
UI
UI values tune layout density and interaction style. Theme normalization is strict; unknown names fall back
to default.
ui.folder_width_cols: default25, clamped8..40ui.compose_vim: defaultfalseui.themepresets:default,ratmail,nord,gruvbox,solarized-dark,solarized-light,dracula,catppuccin-mocha,catppuccin-latte,custom
Custom palette keys ([ui.palette]):
base_fg, base_bg, border, bar_fg, bar_bg, accent, warn, error, selection_bg, selection_fg, link, muted
Custom palette values must be hex colors (for example #88c0d0). Invalid color strings are
ignored for the specific palette key.
Spell
Spellcheck is backed by Hunspell dictionaries. You can control language and custom dictionary path here.
spell.lang: defaulten_USspell.dir: optional dictionary directoryspell.ignore: list of words, normalized to lowercase
Use spell.ignore for recurring names, project terms, or acronyms that should not generate
review noise during compose.
CLI
CLI configuration is straightforward and lives in ratmail.toml.
cli.enabled: defaultfalsecli.default_account: optional default CLI account- If
cli.default_accountis unset, pass--accounton commands that need it
TUI Guide
The TUI is optimized around quick keyboard workflows: browse folders, triage messages, open focused view, and compose without leaving the terminal.
Main modes:
- List/View panes
- Focus mode for full message scrolling
- Compose mode
- Overlays: search, links, attachments, spellcheck, bulk actions, confirmations
Primary keys
The help bar in-app shows compact bindings by default and expands with ?. Most list navigation
works in both Vim-style and arrow-key style.
Tab,h,l: move focus panesj/k: move selectionSpace: select message and advanceEnter: open message / open selected bulk actionsv: toggle rendered/text viewp: toggle preview pane/: search overlays: sync selected foldero: backfill older messages[/]: switch account tab?: toggle expanded helpq: quit
Behavior note: selecting with Space enables bulk actions. Press Enter with a
selection set to open bulk action overlay.
Compose
Compose supports attachments, spell tools, reply/forward helpers, and draft confirmation. Sending can be triggered from keyboard without leaving the compose overlay.
Ctrl+SorF5: sendCtrl+A: attach file pickerCtrl+R: remove last attachmentF7: spellcheck overlayCtrl+QorEsc: close compose (draft confirm if content exists)
Vim compose mode (ui.compose_vim = true):
- Body starts in Normal mode,
Esctoggles Insert/Normal - Normal mode supports movement/edit ops (
h j k l w b 0 $ x dd,i a A I o O)
When Vim compose mode is off, compose behaves as a standard text-input workflow with tab-cycle focus.
Search syntax
Search accepts mixed plain text and structured field filters in one query. Filters narrow results before rendering message lists.
- Text terms: match
from,subject, and preview from:,subject:,to:,date:since:,before:(dateparse-compatible dates)att:,file:,filename:for attachment namestype:,mime:for attachment MIME or extension
Examples:
from:alice subject:invoice since:2026-01-01
to:team@example.com att:report type:pdf
urgent deploy rollbackCLI Guide
The CLI is designed for agent/tool automation. Every response is machine-readable JSON with explicit success or failure fields.
CLI output is JSON, with schema ratmail.cli.v1.
{
"schema": "ratmail.cli.v1",
"ok": true,
"result": { ... }
} Errors:
{
"schema": "ratmail.cli.v1",
"ok": false,
"error": "..."
} Command tree
Read-only commands and mutation commands share one command tree with direct execution behavior.
ratmail setup
ratmail accounts list
ratmail folders list [--account <name>]
ratmail messages list [filters...]
ratmail message get --id <n> [--body] [--raw] [--attachments] [--fetch]
ratmail message body --id <n> [--fetch]
ratmail message raw --id <n> [--fetch]
ratmail message attachment-save --id <n> --index <i> --path <file> [--fetch]
ratmail message move --id <n> --folder <name>
ratmail message delete --id <n>
ratmail message mark --id <n> (--read|--unread)
ratmail sync [--account <name>] [--folder <name>] [--backfill] [--days <n>] [--wait] [--timeout-secs <n>]
ratmail send --to ... --subject ... --body ... [--cc ...] [--bcc ...] [--attach ...] [--wait] [--timeout-secs <n>] --wait on send and sync blocks for completion events up to --timeout-secs. Without --wait, commands queue work and return quickly.
Message list filters
--unread,--limit,--folder,--account--from,--subject,--to,--date--querywith search syntax above--since/--beforeor--since-ts/--before-ts--attand--att-typeattachment filters
For best performance in automation, pass folder/account explicitly and keep --limit small on
polling loops.
Automation shortcut
Use string commands with --cmd, for example:
ratmail --cmd "messages list --account Personal --unread --limit 20" --cmd is parsed by Ratmail and then re-routed through normal command handling, so behavior is
identical to direct subcommands.
Agent Skills (Claude and Codex)
Ratmail ships skill assets under skills/ in the app repo. These are prompt/workflow packs
meant for agent tooling, not binary plugins loaded by Ratmail itself.
Direct GitHub sources:
Install for Codex/OpenAI-style skills
If your Codex environment reads skills from $CODEX_HOME/skills, fetch directly from GitHub.
For default local setups, use ~/.codex/skills:
mkdir -p "$HOME/.codex/skills/openai/ratmail-cli/agents"
curl -L "https://raw.githubusercontent.com/peter-fm/ratmail/main/skills/openai/ratmail-cli/SKILL.md" \
-o "$HOME/.codex/skills/openai/ratmail-cli/SKILL.md"
curl -L "https://raw.githubusercontent.com/peter-fm/ratmail/main/skills/openai/ratmail-cli/agents/openai.yaml" \
-o "$HOME/.codex/skills/openai/ratmail-cli/agents/openai.yaml" Verify:
ls -R "$HOME/.codex/skills/openai/ratmail-cli" Install for Claude-style skills
Claude clients vary by host/editor. If your setup supports a local filesystem skill folder, fetch the same way:
mkdir -p "$HOME/.claude/skills"
mkdir -p "$HOME/.claude/skills/ratmail-cli"
curl -L "https://raw.githubusercontent.com/peter-fm/ratmail/main/skills/claude/ratmail-cli/SKILL.md" \
-o "$HOME/.claude/skills/ratmail-cli/SKILL.md" If your Claude client uses a different path, copy the same ratmail-cli folder into that
client-specific skills directory.
Usage note
These skills describe how an agent should operate the Ratmail CLI and map directly to the standard subcommands shown above.
The published skill files are aligned with the current straightforward CLI model.
Troubleshooting
Most CLI failures are configuration, account selection, or connectivity issues.
- CLI disabled: set
[cli].enabled = true. - No account selected: set
cli.default_accountor pass--account. - Message content unavailable: retry with
--fetchfor body/raw/attachments. - Send/sync timeouts: raise
--timeout-secswhen using--wait. - No rendered HTML: terminal image protocol support is required for rendered mode.
If needed, rerun setup to regenerate account and CLI defaults.