2026-05-20 02:50:45 +02:00
2026-05-20 00:54:21 +02:00
2021-04-18 00:06:11 +02:00
2024-12-09 12:04:15 +01:00
2026-05-20 02:36:43 +02:00
2026-05-20 02:36:43 +02:00
2026-05-20 02:13:30 +02:00
2026-05-20 02:36:43 +02:00
2026-05-20 00:54:21 +02:00
2026-02-19 11:05:12 +01:00
2026-05-20 02:50:45 +02:00
2026-05-20 00:54:21 +02:00
2026-05-20 00:54:21 +02:00
2024-12-09 12:04:15 +01:00
2026-05-20 02:36:43 +02:00
2026-05-20 02:36:43 +02:00
2026-05-20 02:13:30 +02:00
2026-05-20 00:54:21 +02:00

Logo

📫 Himalaya

CLI to manage emails

Release Repology Matrix Mastodon

himalaya envelopes list --account posteo -m Archives.FOSS --page 2

screenshot

Important

This README documents Himalaya v2, which is not yet released. If you are running v1 (himalaya v1.2.0 or earlier), refer to the v1.2.0 README instead. The MIGRATION.md guide walks v1 users through the breaking changes.

Table of contents

Features

  • Shared API that maps mailboxes, envelopes, flags, messages and attachments to the active backend
  • Protocol-specific APIs exposing each backend's full surface (himalaya imap/smtp/maildir/jmap…)
  • IMAP support rfc9051 (requires imap feature)
  • JMAP support rfc8620, rfc8621 (requires jmap feature)
  • Maildir support (requires maildir feature)
  • SMTP backend rfc5321 (requires smtp feature)
  • TLS support:
    • native-tls (requires native-tls feature)
    • rustls:
      • AWS-LC crypto provider (requires rustls-aws feature)
      • Ring crypto provider (requires rustls-ring feature)
  • SASL support: anonymous, login, plain, oauthbearer, xoauth2, scram-sha-256
  • Provider discovery wizard powered by io-discovery: Thunderbird Autoconfiguration, PACC and RFC 6186 SRV lookups
  • TOML configuration with multi-account support
  • JSON output via --json

Himalaya CLI is written in Rust, and relies on cargo features to enable or disable functionalities. Default features can be found in the features section of the Cargo.toml, or on docs.rs.

Installation

Pre-built binary

Himalaya CLI can be installed with the install.sh installer:

As root:

curl -sSL https://raw.githubusercontent.com/pimalaya/himalaya/master/install.sh | sudo sh

As a regular user:

curl -sSL https://raw.githubusercontent.com/pimalaya/himalaya/master/install.sh | PREFIX=~/.local sh

These commands install the latest binary from the GitHub releases section.

If you want a more up-to-date version than the latest release, check out the releases GitHub workflow and look for the Artifacts section. You will find a pre-built binary matching your OS. These pre-built binaries are built from the master branch.

Such binaries are built with the default cargo features. If you need more features, please use another installation method.

Cargo

Himalaya CLI can be installed with cargo:

cargo install himalaya --locked

With only IMAP support:

cargo install himalaya --locked --no-default-features --features imap

You can also use the git repository for a more up-to-date (but less stable) version:

cargo install --locked --git https://github.com/pimalaya/himalaya.git

Arch Linux

Himalaya CLI can be installed on Arch Linux with either the community repository:

pacman -S himalaya

or the user repository:

git clone https://aur.archlinux.org/himalaya-git.git
cd himalaya-git
makepkg -isc

If you use yay, it is even simplier:

yay -S himalaya-git

Homebrew

Himalaya CLI can be installed with Homebrew:

brew install himalaya

Note: cargo features are not compatible with brew. If you need a different feature set, please use another installation method.

Scoop

Himalaya CLI can be installed with Scoop:

scoop install himalaya

Fedora Linux/CentOS/RHEL

Himalaya CLI can be installed on Fedora Linux/CentOS/RHEL via the COPR repo:

dnf copr enable atim/himalaya
dnf install himalaya

Nix

Himalaya CLI can be installed with Nix:

nix-env -i himalaya

You can also use the git repository for a more up-to-date (but less stable) version:

nix-env -if https://github.com/pimalaya/himalaya/archive/master.tar.gz

Or, from within the source tree checkout:

nix-env -if .

If you have the Flakes feature enabled:

nix profile install github:pimalaya/himalaya

Or, from within the source tree checkout:

nix profile install

You can also run Himalaya directly without installing it:

nix run github:pimalaya/himalaya

Sources

git clone https://github.com/pimalaya/himalaya
cd himalaya
nix develop --command cargo build --release

Binaries are available under the target/release folder.

Configuration

Just run himalaya. When no configuration file is found, the wizard prompts for an account name and email address, runs provider discovery (PACC → Thunderbird Autoconfiguration → RFC 6186 SRV), fills the IMAP/SMTP (or JMAP) prompts with the discovered defaults, and writes the result to disk.

Accounts can be (re)configured later with himalaya account configure <name>. The wizard skips discovery in this mode: it reuses the existing values as prompt defaults.

You can also write the configuration by hand:

  • Copy the documented ./config.sample.toml
  • Paste it into one of:
    • $XDG_CONFIG_HOME/himalaya/config.toml
    • $HOME/.config/himalaya/config.toml
    • $HOME/.himalayarc
  • Comment or uncomment the options you want

…or pass -c <PATH> / set HIMALAYA_CONFIG=<PATH>. Multiple paths can be passed at once, separated by :; the first is the base and the rest are deep-merged on top.

Usage

Shared API

Backend-agnostic commands operate on the account's first configured backend, or the one selected with -b/--backend:

himalaya mailboxes list
himalaya envelopes list -m INBOX --page 2
himalaya envelopes search from alice and after 2026-01-01 order by date desc
himalaya flags add -m INBOX --flag seen 1:3,5
himalaya messages copy --from INBOX --to Archives 42
himalaya attachments download -m INBOX 42

When the inbox alias is configured under [mailbox.alias], -m/--mailbox becomes optional: shared commands fall back to that id. With [mailbox.alias] inbox = "INBOX", the calls above shorten to envelopes list --page 2, flags add --flag seen 1:3,5, etc.

envelopes list is plain pagination, ordered by date descending. To filter or sort, use envelopes search with a trailing query covering date, after, from, to, subject, body, flag conditions (combined with and, or, not, grouped with parens) and an order by date|from|to|subject [asc|desc] sort chain. Date clauses target the Date: header (sent-at) on every backend.

The shared surface is a strict least-common-denominator subset across IMAP, JMAP and Maildir. Operations that do not generalize (mailbox roles, attribute flags, JMAP-specific queries…) live under the protocol-specific subcommands.

Protocol-specific APIs

Each backend exposes its full native API under its own subgroup:

himalaya imap mailboxes select INBOX
himalaya imap mailboxes status INBOX
himalaya imap mailboxes subscribe INBOX

himalaya jmap mailboxes query --role drafts
himalaya jmap identity get
himalaya jmap vacation get

himalaya maildir create Archives
himalaya maildir messages save -m ~/Mail/example/Archives < message.eml

himalaya smtp messages send < message.eml

The -b/--backend flag is only consumed by the shared commands; protocol subcommands always use their own backend.

Composing messages

The built-in messages compose / reply / forward commands cover simple cases via CLI flags:

himalaya messages compose --from me@example.org --to you@example.org \
    --subject "Hello" --body "Hi!" --send

For richer composition (multipart MIME, MML directives, signing/encryption, editor-driven workflows…), wire a user-defined composer in [message.composer.*] and invoke it with the -with variants. For example, with mml:

[message.composer.mml]
command = "mml compose"
default = true
himalaya messages compose-with
himalaya messages reply-with -m INBOX 42 --send
himalaya messages forward-with -m INBOX 42 --send
himalaya messages mailto 'mailto:bob@example.org?subject=Hi&body=Hello'

messages mailto <URI> parses an RFC 6068 mailto: URI (recipient list in the path, to / cc / bcc / subject / body query parameters), builds a draft RFC 5322 skeleton with those headers pre-filled, then pipes it on stdin to the named (or default) composer for editing. The composer's output is routed through --save / --send like the other -with variants. Useful as a desktop mailto: handler.

Reading messages

The built-in messages read command renders a message with himalaya's default formatter. For custom rendering, declare a reader in [message.reader.*] and call read-with:

[message.reader.mml]
command = "mml read"
default = true
himalaya messages read-with -m INBOX 42

Re-using sessions

Each invocation opens a fresh TCP+TLS+SASL session by default. To amortize the handshake across many commands, pair himalaya with sirup: sirup exposes a pre-authenticated IMAP/SMTP session over a Unix socket, and himalaya can point its imap.server / smtp.server at that socket.

Interfaces

These interfaces are built at the top of Himalaya CLI to improve the User Experience:

FAQ

How different is it from aerc, mutt or alpine?

Aerc, mutt and alpine can be categorized as Terminal User Interfaces (TUI). When the program is executed, your terminal is locked into an event loop and you interact with your emails using keybinds.

Himalaya is a Command-Line Interface (CLI). There is no event loop: you interact with your emails using shell commands, in a stateless way.

A dedicated TUI (himalaya-tui) is in active development on top of the same Pimalaya libraries.

How are secrets resolved?

Every *.passwd / *.password / *.token field accepts either a raw literal or a shell command that prints the secret on stdout. The raw form is convenient for testing but should not be used in production:

imap.sasl.plain.passwd.raw = "***"
imap.sasl.plain.passwd.command = "pass show example"
imap.sasl.plain.passwd.command = ["pass", "show", "example"]

Native keyring support was removed in v2. Use pimalaya/mimosa (or pass, secret-tool, gopass…) as the command.

How is OAuth 2.0 handled?

v2 does not ship OAuth flows. Use pimalaya/ortie (or any other token broker) to obtain an access token, then plug it as a command returning the token on stdout. For JMAP, point jmap.auth.bearer.token.command at the broker; for IMAP/SMTP, route the bearer through a SASL mechanism that consumes a command-sourced password.

How does the wizard discover IMAP/SMTP/JMAP configs?

The wizard runs three discovery mechanisms in series on the email address domain; the first non-empty hit wins:

  1. PACC draft-ietf-mailmaint-pacc-02: well-known JSON, digest-verified against the _ua-auto-config TXT record.
  2. Thunderbird Autoconfiguration: ISP main / well-known / ISPDB lookups, then MX-based retry, then the mailconf=<URL> TXT redirect.
  3. RFC 6186 SRV: _imap._tcp, _imaps._tcp, _submission._tcp lookups assembled into a single report.

See io-discovery for the full chain.

How to debug Himalaya CLI?

Use --log-level <level> (alias --log) where <level> is one of off, error, warn, info, debug, trace:

himalaya --log trace mailboxes list

The RUST_LOG environment variable is consulted when --log is not passed, and supports per-target filters (see the env_logger documentation). RUST_BACKTRACE=1 enables full error backtraces.

Logs are written to stderr, so they can be redirected easily to a file:

himalaya --log trace mailboxes list 2>/tmp/himalaya.log

You can also send logs straight to a file via --log-file <path>:

himalaya --log trace --log-file /tmp/himalaya.log mailboxes list
How to disable color output?

Set NO_COLOR=1 in your environment.

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

Languages
Rust 98.9%
Nix 0.8%
Shell 0.3%