Skip to content

Quick start

After installing, you have one binary: mxr. The TUI is what you’ll use most; the CLI handles scripting and one-off operations. Both talk to the same daemon.

Start with a realistic local demo before connecting your own mail:

Terminal window
mxr demo

This creates a separate mxr-demo config, database, socket, and daemon. It seeds a 50k-message, two-account synthetic inbox with repeat senders, threads, attachments, links, images, newsletters, promos, spam, suspicious inbox mail, receipts, alerts, and demo rules. You get search, labels, summaries, sender profiles, analytics, and keyboard triage without granting access to your real inbox.

mxr demo prints progress and waits on progress, not on a wall clock — the seed and sync waits give up only if nothing has moved for ten minutes, however long the work itself takes, and the analytics prewarm streams progress with no deadline at all. Expect a couple of minutes end to end on a laptop, longer on a small cloud VM; the mailbox is seeded and the demo is announced as active around the 30–45 second mark. After that it prewarms analytics and Wrapped so the first click on each surface is instant, and watches semantic indexing briefly (about 90 seconds) before handing over — embedding 50k messages takes far longer than anyone should wait, so it reports how far it got and continues in the background (mxr semantic status). Search works throughout; semantic results sharpen as vectors land. Press Ctrl-C any time during the prewarm phase and start using the demo. Pass --messages for a smaller mailbox (mxr demo --messages 5000).

The demo also pre-seeds every “empty queue” surface so the first click on any feature shows something useful: snippets, signatures, custom labels, saved searches, screener decisions, snoozed messages, reply-later flags, and a couple of in-progress drafts. LLM-backed features (summarize, briefing, ask, draft-assist, voice, decisions, commitments) are answered by an in-process canned provider, so the demo works fully offline — no OPENAI_API_KEY needed, and your real LLM credentials are never invoked even if [llm] is configured.

Demo mode is sticky. Once mxr demo finishes, every subsequent mxr command (mxr search, mxr cat, mxr archive, mxr web, …) automatically targets the demo profile. The TUI status bar and the web app’s topbar both show a DEMO chip so you always know which profile you’re on. Exit with:

Terminal window
mxr demo stop

Other demo subcommands:

Terminal window
mxr demo status # is demo active? where are its files?
mxr demo reset # wipe demo data so the next `mxr demo` re-seeds from scratch
mxr demo --reset # equivalent: reset + restart in one step
mxr demo --no-tui # seed + sync without launching the TUI

For Gmail on your local machine:

Terminal window
mxr accounts add gmail

The wizard prompts for an account name and your Gmail address, then opens a browser to authorize. If you’re SSH’d into a remote box, see the Gmail setup notes on SSH-friendly flows.

For Outlook.com, Hotmail, or Live:

Terminal window
mxr accounts add outlook

For a Microsoft 365 work or school account:

Terminal window
mxr accounts add outlook-work

For IMAP+SMTP non-interactively (e.g. Gmail with an app password):

Terminal window
MXR_IMAP_PASSWORD="$APP_PASSWORD" MXR_SMTP_PASSWORD="$APP_PASSWORD" \
mxr accounts add imap-smtp \
--account-name personal \
--email you@gmail.com \
--imap-host imap.gmail.com \
--imap-username you@gmail.com \
--smtp-host smtp.gmail.com \
--smtp-username you@gmail.com

MXR_IMAP_PASSWORD / MXR_SMTP_PASSWORD env vars resolve when stdin is not a TTY — handy for CI. You can also pass --imap-password / --smtp-password literal values if you don’t mind shell history.

mxr sync starts a sync in the daemon and returns as soon as it has started. The account keeps syncing afterwards, so you can start reading mail right away. Add --wait to stay until that account reports idle:

Terminal window
mxr sync # trigger, return immediately
mxr sync --wait # trigger, then wait for idle
mxr sync --wait --wait-timeout-secs 900 # a first backfill needs a longer wait

What you get: with --wait, a live progress line on stderr each time the count moves, then exit 0 once the account is idle.

personal: 3,000/50,000 — Stored 3000 messages

The denominator appears only when the provider can say how much is left. Gmail and IMAP cannot count the remainder without paying for it, so there you get a bare running count.

--wait also exits non-zero if the sync it started failed — an error the account was already carrying before the trigger does not count.

Check on it any time without waiting:

Terminal window
mxr sync --status
mxr sync --status --format json | jq '.[] | {account_name, sync_in_progress, progress}'

Subsequent syncs are incremental and run automatically in the background once the daemon is up.

Terminal window
mxr

j/k to navigate, <Enter> to open, R for reader mode, Ctrl-p for the command palette, / for search, ? for help.

Terminal window
# Search
mxr search "from:alice@example.com is:unread" --format json | jq .
# Read the first match
mxr cat <message-id>
# Reply and send
mxr reply <message-id> --body "On it." --yes
# Archive a query
mxr archive --search "label:newsletters older_than:30d" --dry-run
mxr archive --search "label:newsletters older_than:30d" --yes

Most read/list/status/mutation surfaces accept --format json; the generated CLI reference lists the exact formats per command.

Run into something? See Troubleshooting.