Skip to content

himalaya

himalaya is a Rust CLI to manage emails.

Features:

  • Multi-accounting
  • Interactive configuration via wizard (requires wizard feature)
  • Mailbox, envelope, message and flag management
  • Message composition based on $EDITOR
  • IMAP backend (requires imap feature)
  • Maildir backend (requires maildir feature)
  • Notmuch backend (requires notmuch feature)
  • SMTP backend (requires smtp feature)
  • Sendmail backend (requires sendmail feature)
  • Global system keyring for managing secrets (requires keyring feature)
  • OAuth 2.0 authorization (requires oauth2 feature)
  • JSON output via --output json
  • PGP encryption:
  • via shell commands (requires pgp-commands feature)
  • via GPG bindings (requires pgp-gpg feature)
  • via native implementation (requires pgp-native feature)

Cons:

  • Documentation is inexistent, you have to dive into the --help to understand stuff.

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.

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.

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

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

Configuration

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

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

  • Copy the content of the documented ./config.sample.toml
  • Paste it in a new file ~/.config/himalaya/config.toml
  • Edit, then comment or uncomment the options you want

If using mbrsync

My generic configuration for an mbrsync account is:

[accounts.account_name]

email = "lyz@example.org"
display-name = "lyz"
envelope.list.table.unseen-char = "u"
envelope.list.table.replied-char = "r"
backend.type = "maildir"
backend.root-dir = "/home/lyz/.local/share/mail/lyz-example"
backend.maildirpp = false
message.send.backend.type = "smtp"
message.send.backend.host = "example.org"
message.send.backend.port = 587
message.send.backend.encryption = "start-tls"
message.send.backend.login = "lyz"
message.send.backend.auth.type = "password"
message.send.backend.auth.command = "pass show mail/lyz.example"

Once you've set it then you need to fix the INBOX directory.

Then you can check if it works by running himalaya envelopes list -a lyz-example

Vim plugin installation

Using lazy:

return {
  {
    "pimalaya/himalaya-vim",
  },
}

You can then run :Himalaya account_name and it will open himalaya in your editor.

Configure the account bindings

To avoid typing :Himalaya account_name each time you want to check the email you can set some bindings:

return {
  {
    "pimalaya/himalaya-vim",
    keys = {
      { "<leader>ma", "<cmd>Himalaya account_name<cr>", desc = "Open account_name@example.org" },
      { "<leader>ml", "<cmd>Himalaya lyz<cr>", desc = "Open lyz@example.org" },
    },
  },
}

Setting the description is useful to see the configured accounts with which-key by typing <leader>m and waiting.

Configure extra bindings

The default plugin doesn't yet have all the bindings I'd like so I've added the next ones:

  • In the list of emails view:
  • dd in normal mode or d in visual: Delete emails
  • q: exit the program

  • In the email view:

  • d: Delete email
  • q: Return to the list of emails view

If you want them too set the next config:

return {
  {
    "pimalaya/himalaya-vim",
    config = function()
      vim.api.nvim_create_augroup("HimalayaCustomBindings", { clear = true })
      vim.api.nvim_create_autocmd("FileType", {
        group = "HimalayaCustomBindings",
        pattern = "himalaya-email-listing",
        callback = function()
          -- Bindings to delete emails
          vim.api.nvim_buf_set_keymap(0, "n", "dd", "<plug>(himalaya-email-delete)", { noremap = true, silent = true })
          vim.api.nvim_buf_set_keymap(0, "x", "d", "<plug>(himalaya-email-delete)", { noremap = true, silent = true })
          -- Bind `q` to close the window
          vim.api.nvim_buf_set_keymap(0, "n", "q", ":bd<CR>", { noremap = true, silent = true })
        end,
      })

      vim.api.nvim_create_augroup("HimalayaEmailCustomBindings", { clear = true })
      vim.api.nvim_create_autocmd("FileType", {
        group = "HimalayaEmailCustomBindings",
        pattern = "mail",
        callback = function()
          -- Bind `q` to close the window
          vim.api.nvim_buf_set_keymap(0, "n", "q", ":q<CR>", { noremap = true, silent = true })
          -- Bind `d` to delete the email and close the window
          vim.api.nvim_buf_set_keymap(
            0,
            "n",
            "d",
            "<plug>(himalaya-email-delete):q<CR>",
            { noremap = true, silent = true }
          )
        end,
      })
    end,
  },
}

Configure email fetching from within vim

Fetching emails from within vim is not yet supported, so I'm manually refreshing by account:

return {
  {
    "pimalaya/himalaya-vim",
    keys = {
      -- Email refreshing bindings
      { "<leader>rj", ':lua FetchEmails("lyz")<CR>', desc = "Fetch lyz@example.org" },
    },
    config = function()
      function FetchEmails(account)
        vim.notify("Fetching emails for " .. account .. ", please wait...", vim.log.levels.INFO)
        vim.cmd("redraw")
        vim.fn.jobstart("mbsync " .. account, {
          on_exit = function(_, exit_code, _)
            if exit_code == 0 then
              vim.notify("Emails for " .. account .. " fetched successfully!", vim.log.levels.INFO)
            else
              vim.notify("Failed to fetch emails for " .. account .. ". Check the logs.", vim.log.levels.ERROR)
            end
          end,
        })
      end
    end,
  },
}

You still need to open again :Himalaya account_name as the plugin does not reload if there are new emails.

Show notifications when emails arrive

You can set up mirador to get those notifications.

Not there yet

Troubleshooting

Cannot find maildir matching name INBOX

mbrsync uses Inbox instead of the default INBOX so it doesn't find it. In theory you can use folder.alias.inbox = "Inbox" but it didn't work with me, so I finally ended up doing a symbolic link from INBOX to Inbox.

Cannot find maildir matching name Trash

That's because the Trash directory does not follow the Maildir structure. I had to create the cur tmp and new directories.

References