Ratmail Docs

Complete Guide

Install, onboard, configure, operate the TUI, and automate with the straightforward JSON CLI.

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-complete after 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 ratmail on your PATH first, falling back to a binary placed in Contents/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-height and window-width in 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 = 90
  • imap.fetch_chunk_size = 10

If AI CLI is enabled, setup writes:

  • [cli] defaults into ratmail.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:

  1. ./ratmail.toml
  2. $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_path resolves under $XDG_STATE_HOME/ratmail (or ~/.local/state/ratmail)
  • If db_path is omitted, default is ratmail-<slug-account-name>.db
  • imap.fetch_chunk_size is clamped to 1..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, default true
  • render.width_px: default 800 (runtime geometry can override)
  • render.render_scale: default 1.5, clamped 0.25..4.0
  • render.tile_height_px_side: default 1000
  • render.tile_height_px_focus: default 60

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: default true
  • send.font_family: default Arial, sans-serif
  • send.font_size_px: default 14, clamped 8..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: default 25, clamped 8..40
  • ui.compose_vim: default false
  • ui.theme presets: 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: default en_US
  • spell.dir: optional dictionary directory
  • spell.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: default false
  • cli.default_account: optional default CLI account
  • If cli.default_account is unset, pass --account on 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 panes
  • j/k: move selection
  • Space: select message and advance
  • Enter: open message / open selected bulk actions
  • v: toggle rendered/text view
  • p: toggle preview pane
  • /: search overlay
  • s: sync selected folder
  • o: backfill older messages
  • [/]: switch account tab
  • ?: toggle expanded help
  • q: 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+S or F5: send
  • Ctrl+A: attach file picker
  • Ctrl+R: remove last attachment
  • F7: spellcheck overlay
  • Ctrl+Q or Esc: close compose (draft confirm if content exists)

Vim compose mode (ui.compose_vim = true):

  • Body starts in Normal mode, Esc toggles 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 names
  • type:, 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 rollback

CLI 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
  • --query with search syntax above
  • --since/--before or --since-ts/--before-ts
  • --att and --att-type attachment 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_account or pass --account.
  • Message content unavailable: retry with --fetch for body/raw/attachments.
  • Send/sync timeouts: raise --timeout-secs when using --wait.
  • No rendered HTML: terminal image protocol support is required for rendered mode.

If needed, rerun setup to regenerate account and CLI defaults.