CLI to watch mailbox changes, written in Rust
Caution
Mirador is in active development and currently shipped as v0.1.x. Expect breaking changes between releases until stabilization. See MIGRATION.md if you ran a pre-v0.1.0 build.
- Remote backends: IMAP via RFC 2177 IDLE, JMAP via RFC 8620 §7.2 EventSource push
- Local backend: Maildir specs via filesystem notifications
- Watch events:
on-message-added,on-message-removed,on-flags-added,on-flags-removed(flag hooks accept an optionalflags = [...]filter) - Hook actions: system notification via notify-rust and shell command (TOML string handed to
/bin/sh -con Unix /cmd /Con Windows; or a TOML[program, args…]list spawned directly with no shell) - Shell-style placeholders (
$name/${name}) in hook strings:id,mailbox,subject,sender,sender_name,sender_address,recipient,recipient_name,recipient_address,flag,flags. Shell-command hooks receive them as environment variables, so the shell's own expansion does the substitution: write"$subject"(quoted) for safe whitespace handling. - Simple auth support for IMAP: anonymous, login, plain, oauthbearer, xoauth2, scram-sha-256
- HTTP auth support for JMAP: basic, bearer
- TLS support:
- Rustls with ring crypto
- Rustls with aws crypto (requires
rustls-awsfeature) - Native TLS (requires
native-tlsfeature)
- Shared configuration file with
himalayaandhimalaya-tui: the same[accounts.<name>]block loads on all three binaries (see Configuration)
Tip
Mirador is written in Rust and uses cargo features to gate backend support. The default feature set is declared in Cargo.toml.
Mirador is not yet released; the only way to get a pre-built binary today is to check out the releases GitHub workflow and look for the Artifacts section.
Note
Such binaries are built with the default cargo features. If you need specific features, please use another installation method.
cargo install --locked --git https://github.com/pimalaya/mirador.git
With only IMAP support:
cargo install --locked --git https://github.com/pimalaya/mirador.git \
--no-default-features \
--features imap,rustls-ring
If you have the Flakes feature enabled:
nix profile install github:pimalaya/mirador
Or run without installing:
nix run github:pimalaya/mirador
git clone https://github.com/pimalaya/mirador
cd mirador
nix run
Copy config.sample.toml to $XDG_CONFIG_HOME/mirador/config.toml and edit it. Mirador does not ship an interactive wizard; the sample documents every backend block and hook with inline comments.
A persistent configuration is loaded from the first valid path among:
$XDG_CONFIG_HOME/mirador/config.toml$HOME/.config/mirador/config.toml$HOME/.miradorrc
These are the same paths the himalaya CLI and himalaya-tui look at: one TOML file backs all three binaries, starting from himalaya CLI v2. Each backend lives under its own protocol key (imap.*, jmap.*, maildir.*), declared as flat dotted entries under [accounts.<name>]. Mirador-only fields (mailbox, the hooks.on-* tables) coexist with the shared keys and are silently ignored by the other binaries.
Warning
A pre-v0.1.0 mirador configuration file is not compatible with v0.1.0: the schema differs. See MIGRATION.md (or rewrite the file using config.sample.toml as a template) before pointing v0.1.0 at it.
Override the path with -c <PATH> or MIRADOR_CONFIG=<PATH>; multiple paths can be passed at once, separated by :. The first one is the base and the rest are deep-merged on top.
An account may declare more than one of the imap, jmap, maildir blocks (so the same TOML file can drive mirador and himalaya against different backends). Mirador opens exactly one connection per watch, so the active backend is picked at startup:
-b/--backend imap | jmap | maildirpins the active backend; the command bails when the account has no matching block.-b auto(default) picks the first configured block in this order: IMAP, then JMAP, then Maildir.
Mirador fires zero or more hooks per watch event kind. Each hook config can declare a system notification, a shell command, or both:
# String `cmd`: handed to /bin/sh -c on Unix, cmd /C on Windows.
# Placeholders are env vars; the shell does the expansion.
hooks.on-message-added.notify = { summary = "New mail from $sender", body = "$subject" }
hooks.on-message-added.cmd = 'echo "$id arrived" >> ~/.local/state/mirador.log'
# List `cmd`: [program, args...] spawned directly. Flag hooks accept
# an optional `flags = [...]` filter (IANA flag name, case-insensitive).
hooks.on-flags-added.flags = ["Seen"]
hooks.on-flags-added.cmd = ["notify-send", "New flag on $id"]Both cmd shapes are decoded by pimalaya_config::command. Notifications use notify-rust (D-Bus / NSUserNotification / Windows toast). Failures are logged at warn so a broken hook never crashes the watcher.
mirador watch # watch the default account's mailbox
mirador -a work watch # watch the `work` account
mirador watch -m Drafts # watch a specific mailbox
mirador -b jmap watch # force JMAP when several backends are configured
mirador check # validate the account against each configured backend
mirador completions bash ./out # generate shell completions
mirador manuals ./out # generate man pages
The watch loop runs until Ctrl+C; the IMAP / JMAP / Maildir driver winds down cleanly (sends IDLE DONE, closes the SSE socket, drops the notify watcher) before the binary exits.
Coming from a pre-v0.1.0 (draft) mirador build? Read MIGRATION.md. The v0.1.0 configuration schema is incompatible with the earlier [accounts.<name>.backend] shape, which is now replaced by parallel imap.* / jmap.* / maildir.* dotted keys aligned with himalaya CLI v2.
Mirador is one of several front-ends to the Pimalaya libraries. See pimalaya/himalaya#interfaces for the full list (CLI, TUI, Vim, Emacs, Raycast).
This project is developed with AI assistance. This section documents how, so users and downstream packagers can make informed decisions.
-
Tools: Claude Code (Anthropic), Opus 4.7, invoked locally with a persistent project-scoped memory and a small set of repo-specific rules.
-
Used for: Refactors, mechanical multi-file edits, boilerplate (feature gates, error enums, derive macros, trait impls), test scaffolding, doc polish, exploratory design conversations.
-
Not used for: Engineering, critical code, git manipulation (commit, merge, rebase…), real-world tests.
-
Verification: Every AI-assisted change is read, compiled, tested, and formatted before commit (
nix develop --command cargo check / cargo test / cargo fmt). Behavioural correctness is verified against the relevant RFC or upstream spec, not assumed from the model output. Tests are never adjusted to fit AI-generated code; the code is adjusted to fit correct behaviour. -
Limitations: AI models occasionally produce code that compiles and passes tests but is subtly wrong: off-by-one errors, missed edge cases, plausible but nonexistent APIs, stale RFC references. The verification workflow catches most of this; it does not catch all of it. Bug reports are welcome and taken seriously.
-
Last reviewed: 30/05/2026
- Chat on Matrix
- News on Mastodon or RSS
- Mail at pimalaya.org@posteo.net
Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:
- 2022 → 2023: NGI Assure
- 2023 → 2024: NGI Zero Entrust
- 2024 → 2026: NGI Zero Core
- 2027 in preparation…
If you appreciate the project, feel free to donate using one of the following providers: