Skip to content

pimalaya/mirador

Repository files navigation

🔭 Mirador Matrix Mastodon

CLI to watch mailbox changes, written in Rust

screenshot

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.

Table of contents

Features

  • 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 optional flags = [...] filter)
  • Hook actions: system notification via notify-rust and shell command (TOML string handed to /bin/sh -c on Unix / cmd /C on 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-aws feature)
    • Native TLS (requires native-tls feature)
  • Shared configuration file with himalaya and himalaya-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.

Installation

Pre-built binary

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

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

Nix

If you have the Flakes feature enabled:

nix profile install github:pimalaya/mirador

Or run without installing:

nix run github:pimalaya/mirador

Sources

git clone https://github.com/pimalaya/mirador
cd mirador
nix run

Configuration

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.

Backend selection

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 | maildir pins 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.

Hooks

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.

Usage

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.

Migration

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.

Interfaces

Mirador is one of several front-ends to the Pimalaya libraries. See pimalaya/himalaya#interfaces for the full list (CLI, TUI, Vim, Emacs, Raycast).

AI disclosure

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

Social

Sponsoring

nlnet

Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:

If you appreciate the project, feel free to donate using one of the following providers:

GitHub Ko-fi Buy Me a Coffee Liberapay thanks.dev PayPal