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.
- Shared API that maps
mailboxes,envelopes,flags,messagesandattachmentsto the active backend - Protocol-specific APIs exposing each backend's full surface (
himalaya imap/smtp/maildir/jmap…) - Remote backend support: IMAP, SMTP, JMAP
- Local (filesystem) backends support: Maildir specs, m2dir specs
- Simple auth support for IMAP/SMTP: 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)
- Discovery support:
- TOML configuration with multi-account support
- Shared configuration file with
himalaya-tui: same[accounts.<name>]blocks load on both binaries (see Configuration) - JSON output via
--json
Tip
Himalaya is written in Rust and uses cargo features to gate backend support. The default feature set is declared in Cargo.toml.
Himalaya can be installed with the 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.
For a more up-to-date version than the latest release, check out the releases GitHub workflow and look for the Artifacts section. These pre-built binaries are built from the master branch.
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/himalaya.git
With only IMAP+SMTP support:
cargo install --locked --git https://github.com/pimalaya/himalaya.git \
--no-default-features \
--features imap,smtp,rustls-ring
From the community repository:
pacman -S himalaya
Or the user repository:
git clone https://aur.archlinux.org/himalaya-git.git
cd himalaya-git
makepkg -isc
Or with yay:
yay -S himalaya-git
brew install himalaya
Note
Cargo features are not compatible with brew. If you need a different feature set, please use another installation method.
scoop install himalaya
From the COPR repo:
dnf copr enable atim/himalaya
dnf install himalaya
If you have the Flakes feature enabled:
nix profile install github:pimalaya/himalaya
Or run without installing:
nix run github:pimalaya/himalaya
git clone https://github.com/pimalaya/himalaya
cd himalaya
nix run
Run himalaya. With no configuration file on disk the wizard prompts for an account name and an email address, runs provider discovery (PACC, then Thunderbird Autoconfiguration, then RFC 6186 SRV), fills the IMAP/SMTP (or JMAP) prompts with the discovered defaults, then writes the result to disk.
A persistent configuration is loaded from the first valid path among:
$XDG_CONFIG_HOME/himalaya/config.toml$HOME/.config/himalaya/config.toml$HOME/.himalayarc
These are the same paths the himalaya-tui TUI looks at: one TOML file backs both binaries. CLI-only fields and TUI-only sections coexist without errors. See config.sample.toml for a documented template.
Override the path with -c <PATH>; multiple paths can be passed at once, separated by :. The first one is the base and the rest are deep-merged on top.
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.
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.
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.
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), chain a standalone composer such as mml into messages send / messages add through a tempfile or bash/zsh process substitution:
# Explicit tempfile, works in plain POSIX sh
mml compose /tmp/draft.eml && himalaya messages send /tmp/draft.eml
# Bash / zsh process substitution, single command, no tempfile
mml compose >(himalaya messages send)
himalaya messages read 42 | mml reply >(himalaya messages send)The path-arg or process-substitution forms keep the composer's stdout connected to the terminal, so any $EDITOR it spawns sees a real tty. The bare-pipe form (mml compose | himalaya messages send) hangs because the editor inherits a pipe on its stdout.
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.
Himalaya CLI is one of several front-ends to the Pimalaya libraries:
- pimalaya/himalaya-tui: official TUI (in active development)
- pimalaya/himalaya-vim: Vim plugin
- dantecatalfamo/himalaya-emacs: Emacs plugin
- jns/himalaya: Raycast extension
- openclaw/openclaw: OpenClaw SKILL
- parisni/dfzf: dfzf integration
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, and is definitely closer to aerc, mutt and alpine.
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:
- PACC draft-ietf-mailmaint-pacc-02: well-known JSON, digest-verified against the
_ua-auto-configTXT record. - Thunderbird Autoconfiguration: ISP main / well-known / ISPDB lookups, then MX-based retry, then the
mailconf=<URL>TXT redirect. - RFC 6186 SRV:
_imap._tcp,_imaps._tcp,_submission._tcplookups assembled into a single report.
See pimconf for the full chain.
How to debug Himalaya?
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.
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: 31/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:

