Installation

Homebrew (macOS and Linux)

Installs a prebuilt binary, with no Rust toolchain needed:

bash
brew tap sylvainHellin/mailypoppins
brew install mailypoppins

Prebuilt binaries

Download the archive for your platform (macOS arm64, Linux x86_64 gnu or static musl) from the GitHub Releases page, verify the .sha256 checksum, and place the mp binary somewhere on your PATH.

Mac app

From 0.11.0. Download mailypoppins-desktop-aarch64-apple-darwin.dmg (Apple silicon only) from the Releases page and drag mailypoppins.app to Applications. The app carries its own mp and starts the daemon with it, so it needs nothing else installed. It is not signed yet: after the first blocked launch, allow it under System Settings, Privacy & Security ("Open Anyway"), or clear the quarantine flag:

bash
xattr -dr com.apple.quarantine /Applications/mailypoppins.app

To use the app's mp from a terminal too, link it onto your PATH instead of installing a second copy, so the CLI, the TUI and the app share one version of the daemon:

bash
ln -s /Applications/mailypoppins.app/Contents/MacOS/mp /usr/local/bin/mp

Later versions arrive in the app. Once a day it checks the Releases page, and when a newer version is out, an "Update to" entry appears at the foot of the sidebar. Choosing it downloads the update, checks its signature and puts it in place while you keep working; "Restart now" then switches to it, daemon included. "Check for updates" in the command palette, the App menu and Settings checks at once. The mp link above follows the update, since it points into the app.

If a daemon of another version is already running, the app says so and offers to restart it with its own. To uninstall, run mp daemon stop, quit the app, and delete it and the link; your mail and config.toml stay where mp config path says.

From source

You need the Rust toolchain installed. If you don't have it yet, install it from rustup.rs. Then clone the repository and build:

bash
git clone https://github.com/sylvainhellin/mailypoppins
cd mailypoppins
cargo install --path .

This installs the mp binary to ~/.cargo/bin/. Make sure that directory is in your PATH.

Setup

Run the interactive setup wizard:

bash
mp config init

The wizard walks through these steps:

  1. Choose a provider: standard IMAP/SMTP, Microsoft 365 with OAuth2, or Graph API (Graph signs in through the built-in mailypoppins app, so press Enter at the client ID and tenant prompts; until the mailypoppins publisher is verified with Microsoft, an administrator of your organisation has to approve the app at the first sign-in, which personal Microsoft accounts do not need).
  2. Enter credentials (server host, port, username, email address).
  3. Automatic connection test to verify everything works.
  4. Mailbox discovery: the wizard lists server mailboxes and lets you assign the inbox, archive, and sent roles.
  5. Config is written to ~/.config/mailypoppins/config.toml.
  6. Password is stored securely in the OS keyring (macOS Keychain, Windows Credential Manager, or Linux Secret Service).

For Microsoft 365 accounts, the wizard runs the OAuth2 device code flow: it displays a URL and a code, you sign in via your browser, and the token is cached locally.

The wizard does not ask about signatures. A signature is a Markdown file in ~/.config/mailypoppins/signatures/: press cs in the TUI to create one and make it the account's default, or write the file by hand. See Signatures.

First Sync

After setup, pull your emails from the server:

bash
mp sync

This syncs all configured mailboxes (inbox, archive, sent, plus any extras). Each message becomes a row in the account's local store (store.sqlite3), with its text, its HTML and its attachments kept beside it as content-addressed blobs. Nothing is written as a mail file tree: the Markdown you read in the TUI is rendered from the row on demand.

Sync is additive by default: it only downloads new messages, deduplicating by Message-ID. The mailbox is enumerated in full every pass, so messages the server no longer lists are pruned from the local store however old they are.

Launch the TUI

Run the binary with no arguments to open the interactive terminal UI:

bash
mp

The TUI shows a sidebar with your mailboxes, an email list, and a headers/body preview pane. Core navigation:

The read-only copy is rendered from the local store on the spot, with the headers as YAML frontmatter and the body below them, and it is deleted again when you leave the editor. It is there for searching, folding and copying a long message with the tools you already know. Edits go nowhere: the store is the source of truth, and nothing reads the copy back.

Each mailbox but Drafts lists one row per conversation: the question in your inbox, your replies in Sent and the draft of your next reply are one row. The row takes the date of its newest message and shows the message count, such as (3). The first Enter on a conversation of several messages expands it in place, newest first, each member labelled with the mailbox it lives in; the next one reads it, and y on a member copies its selector (in the desktop app, a draft's file path). Archive, delete, flag and mark read on a collapsed row act on its messages in the mailbox you are looking at, never on your Sent copies. Press tc to list an account one row per message instead. The TUI remembers the choice in ~/.config/mailypoppins/tui.json. In the desktop app, tc works too, and so does the "Conversation view" switch on the account's Settings card.

The marker column on the left of the list carries one glyph per message: a dot for unread, a reply arrow once you have answered it, a forward arrow once you have forwarded it, and nothing for a message you have only read. Answered and forwarded are kept in step with the server, so a reply you sent from your phone shows up here too.

The TUI watches your inbox in the background (via IMAP IDLE or Graph polling) and fetches new mail automatically when it arrives.

Your First Draft

The draft workflow goes through three stages: draft (writing), approved (reviewed), and sent (delivered). Here is the end-to-end flow from the CLI:

bash
# Create a new draft. It prints the draft's selector:
# mp://work/drafts/9f2c1ab30d5e4471
mp new meeting-followup

# Open it in your editor and fill in recipients, subject, body
mp edit mp://work/drafts/9f2c1ab30d5e4471

# Mark it as reviewed and ready to send
mp mark-approved mp://work/drafts/9f2c1ab30d5e4471

# Send it (the id alone works too, the rest is filled in from your config)
mp send 9f2c1ab30d5e4471

Or from the TUI:

  1. Press cn to create a new draft. It opens in your $EDITOR.
  2. Write your email in Markdown with YAML frontmatter (to, subject, body).
  3. Save and close the editor. The draft appears in the list.
  4. Press x to send. The draft is approved and sent in one step; confirm when prompted.

A draft file looks like this:

meeting-followup.md
---
to: colleague@example.com
cc: team@example.com
subject: "Meeting follow-up"
status: draft
---

Hi,

Here are the action items from today's meeting:

- Item one
- Item two

Let me know if I missed anything.

What's Next