Home

Awesome

<div align="center"> <img src="./logo.svg" alt="Logo" width="128" height="128" /> <h1>📫 Himalaya</h1> <p>CLI to manage emails, based on <a href="https://crates.io/crates/email-lib"><code>email-lib</code></a></p> <p> <a href="https://github.com/pimalaya/himalaya/releases/latest"><img alt="Release" src="https://img.shields.io/github/v/release/pimalaya/himalaya?color=success"/></a> <a href="https://repology.org/project/himalaya/versions"><img alt="Repology" src="https://img.shields.io/repology/repositories/himalaya?color=success"></a> <a href="https://matrix.to/#/#pimalaya:matrix.org"><img alt="Matrix" src="https://img.shields.io/matrix/pimalaya:matrix.org?color=success&label=chat"/></a> </p> </div>
$ himalaya envelope list --account posteo --folder Archives.FOSS --page 2

screenshot

Features

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.

Installation

The v1.0.0 is currently being tested on the master branch, and is the prefered version to use. Previous versions (including GitHub beta releases and repositories published versions) are not recommended.

Pre-built binary

Himalaya CLI v1.0.0 can be installed with a pre-built binary. Find the latest pre-release GitHub workflow and look for the Artifacts section. You should find a pre-built binary matching your OS.

Cargo (git)

Himalaya CLI v1.0.0 can also be installed with cargo:

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

Other outdated methods

These installation methods should not be used until the v1.0.0 is finally released, as they are all (temporarily) outdated:

<details> <summary>Pre-built binary</summary>

Himalaya CLI can be installed with a prebuilt binary:

# 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.

Binaries are built with default cargo features. If you want to enable or disable a feature, please use another installation method.

</details> <details> <summary>Cargo</summary>

Himalaya CLI can be installed with cargo:

$ cargo install himalaya

# With only IMAP support:
$ cargo install himalaya --no-default-features --features imap

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

$ cargo install --git https://github.com/pimalaya/himalaya.git himalaya
</details> <details> <summary>Arch Linux</summary>

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
</details> <details> <summary>Homebrew</summary>

Himalaya CLI can be installed with Homebrew:

$ brew install himalaya
</details> <details> <summary>Scoop</summary>

Himalaya CLI can be installed with Scoop:

$ scoop install himalaya
</details> <details> <summary>Fedora Linux/CentOS/RHEL</summary>

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

$ dnf copr enable atim/himalaya
$ dnf install himalaya
</details> <details> <summary>Nix</summary>

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 himalaya

# or, from within the source tree checkout
$ nix profile install

# you can also run Himalaya directly without installing it:
$ nix run himalaya
</details> <details> <summary>Sources</summary>

Himalaya CLI can be installed from sources.

First you need to install the Rust development environment (see the rust installation documentation):

$ curl https://sh.rustup.rs -sSf | sh

Then, you need to clone the repository and install dependencies:

$ git clone https://github.com/pimalaya/himalaya.git
$ cd himalaya
$ cargo check

Now, you can build Himalaya:

$ cargo build --release

Binaries are available under the target/release folder.

</details>

Configuration

Just run himalaya, the wizard will help you to configure your default account.

You can also manually edit your own configuration, from scratch:

<details> <summary>Proton Mail (Bridge)</summary>

When using Proton Bridge, emails are synchronized locally and exposed via a local IMAP/SMTP server. This implies 2 things:

[accounts.proton]
email = "example@proton.me"

backend = "imap"
imap.host = "127.0.0.1"
imap.port = 1143
imap.encryption = false
imap.login = "example@proton.me"
imap.passwd.raw = "<bridge-imap-p@ssw0rd>"

message.send.backend = "smtp"
smtp.host = "127.0.0.1"
smtp.port = 1025
smtp.encryption = false
smtp.login = "example@proton.me"
smtp.passwd.raw = "<bridge-smtp-p@ssw0rd>"

Keeping your password inside the configuration file is good for testing purpose, but it is not safe. You have 2 better alternatives:

</details> <details> <summary>Gmail</summary>

Google passwords cannot be used directly. There is two ways to authenticate yourself:

Using App Passwords

This option is the simplest and the fastest. First, be sure that:

First create a dedicated password for Himalaya.

[accounts.gmail]
email = "example@gmail.com"

folder.alias.inbox = "INBOX"
folder.alias.sent = "[Gmail]/Sent Mail"
folder.alias.drafts = "[Gmail]/Drafts"
folder.alias.trash = "[Gmail]/Trash"

backend = "imap"
imap.host = "imap.gmail.com"
imap.port = 993
imap.login = "example@gmail.com"
imap.passwd.cmd = "pass show gmail"

message.send.backend = "smtp"
smtp.host = "smtp.gmail.com"
smtp.port = 465
smtp.login = "example@gmail.com"
smtp.passwd.cmd = "pass show gmail"

Keeping your password inside the configuration file is good for testing purpose, but it is not safe. You have 2 better alternatives:

Using OAuth 2.0

This option is the most secure but the hardest to configure. It requires the oauth2 and keyring cargo features.

First, you need to get your OAuth 2.0 credentials by following this guide. Once you get your client id and your client secret, you can configure your Himalaya account this way:

[accounts.gmail]
email = "example@gmail.com"

folder.alias.inbox = "INBOX"
folder.alias.sent = "[Gmail]/Sent Mail"
folder.alias.drafts = "[Gmail]/Drafts"
folder.alias.trash = "[Gmail]/Trash"

backend = "imap"
imap.host = "imap.gmail.com"
imap.port = 993
imap.login = "example@gmail.com"
imap.oauth2.client-id = "<imap-client-id>"
imap.oauth2.auth-url = "https://accounts.google.com/o/oauth2/v2/auth"
imap.oauth2.token-url = "https://www.googleapis.com/oauth2/v3/token"
imap.oauth2.pkce = true
imap.oauth2.scope = "https://mail.google.com/"

message.send.backend = "smtp"
smtp.host = "smtp.gmail.com"
smtp.port = 465
smtp.login = "example@gmail.com"
smtp.oauth2.client-id = "<smtp-client-id>"
smtp.oauth2.auth-url = "https://accounts.google.com/o/oauth2/v2/auth"
smtp.oauth2.token-url = "https://www.googleapis.com/oauth2/v3/token"
smtp.oauth2.pkce = true
smtp.oauth2.scope = "https://mail.google.com/"

# If you want your SMTP to share the same client id (and so the same access token)
# as your IMAP config, you can add the following:
#
# imap.oauth2.client-id = "<client-id>"
# imap.oauth2.client-secret.keyring = "gmail-oauth2-client-secret"
# imap.oauth2.access-token.keyring = "gmail-oauth2-access-token"
# imap.oauth2.refresh-token.keyring = "gmail-oauth2-refresh-token"
#
# imap.oauth2.client-id = "<client-id>"
# imap.oauth2.client-secret.keyring = "gmail-oauth2-client-secret"
# imap.oauth2.access-token.keyring = "gmail-oauth2-access-token"
# smtp.oauth2.refresh-token.keyring = "gmail-oauth2-refresh-token"

Running himalaya configure -a gmail will complete your OAuth 2.0 setup and ask for your client secret.

</details> <details> <summary>Outlook</summary>
[accounts.outlook]
email = "example@outlook.com"

backend = "imap"
imap.host = "outlook.office365.com"
imap.port = 993
imap.login = "example@outlook.com"
imap.passwd.cmd = "pass show outlook"

message.send.backend = "smtp"
smtp.host = "smtp.mail.outlook.com"
smtp.port = 587
smtp.encryption = "start-tls"
smtp.login = "example@outlook.com"
smtp.passwd.cmd = "pass show outlook"

Using OAuth 2.0

This option is the most secure but the hardest to configure. First, you need to get your OAuth 2.0 credentials by following this guide. Once you get your client id and your client secret, you can configure your Himalaya account this way:

[accounts.outlook]
email = "example@outlook.com"

backend = "imap"
imap.host = "outlook.office365.com"
imap.port = 993
imap.login = "example@outlook.com"
imap.oauth2.client-id = "<imap-client-id>"
imap.oauth2.auth-url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
imap.oauth2.token-url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
imap.oauth2.pkce = true
imap.oauth2.scope = "https://outlook.office.com/IMAP.AccessAsUser.All"

message.send.backend = "smtp"
smtp.host = "smtp.mail.outlook.com"
smtp.port = 587
smtp.starttls = true
smtp.login = "example@outlook.com"
smtp.oauth2.client-id = "<smtp-client-id>"
smtp.oauth2.auth-url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
smtp.oauth2.token-url = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
smtp.oauth2.pkce = true
smtp.oauth2.scope = "https://outlook.office.com/SMTP.Send"

# If you want your SMTP to share the same client id (and so the same access token)
# as your IMAP config, you can add the following:
#
# imap.oauth2.client-id = "<client-id>"
# imap.oauth2.client-secret.keyring = "outlook-oauth2-client-secret"
# imap.oauth2.access-token.keyring = "outlook-oauth2-access-token"
# imap.oauth2.refresh-token.keyring = "outlook-oauth2-refresh-token"
#
# imap.oauth2.client-id = "<client-id>"
# imap.oauth2.client-secret.keyring = "outlook-oauth2-client-secret"
# imap.oauth2.access-token.keyring = "outlook-oauth2-access-token"
# smtp.oauth2.refresh-token.keyring = "outlook-oauth2-refresh-token"

Running himalaya configure -a outlook will complete your OAuth 2.0 setup and ask for your client secret.

</details> <details> <summary>iCloud Mail</summary>

From the iCloud Mail support page:

[accounts.icloud]
email = "johnappleseed@icloud.com"

backend = "imap"
imap.host = "imap.mail.me.com"
imap.port = 993
imap.login = "johnappleseed"
imap.passwd.cmd = "pass show icloud"

message.send.backend = "smtp"
smtp.host = "smtp.mail.me.com"
smtp.port = 587
smtp.encryption = "start-tls"
smtp.login = "johnappleseed@icloud.com"
smtp.passwd.cmd = "pass show icloud"
</details>

FAQ

<details> <summary>How to compose a message?</summary>

An email message is a list of headers (key: val) followed by a body. They form together a template:

Header: value
Header: value
Header: value

Body

Headers and body must be separated by an empty line.

Headers

Here a non-exhaustive list of valid email message template headers:

An address can be:

Multiple address are separated by a coma ,: user@domain, Name <user@domain>, "Name" <user@domain>.

Plain text body

Email message template body can be written in plain text. The result will be compiled into a single text/plain MIME part:

From: alice@localhost
To: Bob <bob@localhost>
Subject: Hello from Himalaya

Hello, world!

MML boby

Email message template body can also be written in MML. The MIME Meta Language was introduced by the Emacs mml ELisp module. Pimalaya ported it in Rust.

A raw email message is structured according to the MIME standard. This standard produces verbose, non-friendly messages. Here comes MML: it simplifies the way email message body are structured. Thanks to its simple XML-based syntax, it allows you to easily add multiple parts, attach a binary file, or attach inline image to your body without dealing with the MIME standard.

For instance, this MML template:

From: alice@localhost
To: bob@localhost
Subject: MML simple

<#multipart type=alternative>
This is a plain text part.
<#part type=text/enriched>
<center>This is a centered enriched part</center>
<#/multipart>

compiles into the following MIME Message:

Subject: MML simple
To: bob@localhost
From: alice@localhost
MIME-Version: 1.0
Date: Tue, 29 Nov 2022 13:07:01 +0000
Content-Type: multipart/alternative;
 boundary="4CV1Cnp7mXkDyvb55i77DcNSkKzB8HJzaIT84qZe"

--4CV1Cnp7mXkDyvb55i77DcNSkKzB8HJzaIT84qZe
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit

This is a plain text part.
--4CV1Cnp7mXkDyvb55i77DcNSkKzB8HJzaIT84qZe
Content-Type: text/enriched
Content-Transfer-Encoding: 7bit

<center>This is a centered enriched part</center>
--4CV1Cnp7mXkDyvb55i77DcNSkKzB8HJzaIT84qZe--

See more examples at pimalaya/core/mml.

</details> <details> <summary>How to add attachments to a message?</summary>

Read first about the FAQ: How to compose a message?.

From: alice@localhost
To: bob@localhost
Subject: How to attach stuff

Regular binary attachment:
<#part filename=/path/to/file.pdf><#/part>

Custom file name:
<#part filename=/path/to/file.pdf name=custom.pdf><#/part>

Inline image:
<#part disposition=inline filename=/path/to/image.png><#/part>

See more examples at pimalaya/core/mml.

</details> <details> <summary>How to debug Himalaya CLI?</summary>

The simplest way is to use --debug and --trace arguments.

The advanced way is based on environment variables:

Logs are written to the stderr, which means that you can redirect them easily to a file:

RUST_LOG=debug himalaya 2>/tmp/himalaya.log
</details> <details> <summary>How the wizard discovers IMAP/SMTP configs?</summary>

All the lookup mechanisms use the email address domain as base for the lookup. It is heavily inspired from the Thunderbird Autoconfiguration protocol. For example, for the email address test@example.com, the lookup is performed as (in this order):

  1. check for autoconfig.example.com
  2. look up of example.com in the ISPDB (the Thunderbird central database)
  3. look up MX example.com in DNS, and for mx1.mail.hoster.com, look up hoster.com in the ISPDB
  4. look up SRV example.com in DNS
  5. try to guess (imap.example.com, smtp.example.com…)
</details>

Sponsoring

nlnet

Special thanks to the NLnet foundation and the European Commission that helped the project to receive financial support from:

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