Added
-
Added
message.send.save-copy, the mailbox a sent message is copied to when--saveis not passed, and--no-saveonmessage send,compose,replyandforwardto skip it.It takes a mailbox name, alias or role, and the v1
truestill reads as thesentmailbox. Leave it unset on Gmail and Microsoft Graph, which file the sent message themselves. -
Added
envelope.list.datetime-relative, rendering recent dates relative to today: the time for today,yesterday, then the weekday for the past week, older dates falling back todatetime-fmt([#510]). -
Added
proxy, a per-account SOCKS5 or HTTP proxy every network backend connects through, and<backend>.proxyto override it for one backend ([#742]).The password is a secret like any credential, so it stays out of the URL. Without either, the
all_proxyandhttps_proxyenvironment variables are still read. -
Added
--posting-style nonetomessage replyandmessage forward, sending the written body without quoting the source. -
Added a decoded template to
message compose,message replyandmessage forwardunder--jsonwhen neither saving nor sending.It carries
from,to,cc,bcc,subjectand abodywithout the signature, so an editor can lay the message out and hand it back through the flags. -
Added
messages-added-detailstogmail history list --json, preserving each arrival's message id, optional thread id and supplied label ids alongside the existing message-id arrays. -
Added
message addon Gmail, inserting the raw message throughmessages.insertwith the mailbox and flags as labels, dated by itsDate:header. -
Added
envelope searchon Microsoft Graph, its filter translated to a KQL$search. Flag clauses are refused, Graph accepting no$filterbeside$search, and the sort applies within the page. -
A pimdir write now shows on the next read instead of on the next sync.
A pimdir store is a replica the sync engine owns, so Himalaya appends its writes to the store's queue rather than apply them. A read used to project the committed index alone, so flagging a message lost the flag from the listing until Neverest ran.
Reads now overlay the pending queue, so a staged flag, edit, deletion, move or copy shows straight away. All of them address a message that already exists and keeps its public id, so nothing changed about how a message is addressed.
A staged creation has no id until the sync engine applies it, so it can be no envelope and does not list.
envelope listreports how many the mailbox has and where to see them, andenvelope searchreports none, a queued message never having been matched against the query. -
himalaya pimdir queue listandhimalaya pimdir queue cancel, for the staged creations the mailbox listing cannot show.The
pimdirbinary is kind-agnostic and prints ids, hashes and flags. Himalaya holds the mail conventions and the blobs, so it renders a queued message with its flags, subject, recipient and the moment it was queued.cancelretracts one by the row id that view prints, which is the only way back for a message that does not exist yet: a staged flag or move is undone by doing the opposite.Cancelling is the store owner's write, so it takes that role for the length of the call and reports a sync in flight rather than wait on it.
-
Added sending from a pimdir account: the message is queued in the store for its owner to send, the one holding the credentials.
The row is filed under the
--savemailbox, else undermailbox.alias.sent, and shows inpimdir queue list. The command prints its row id,queueIdunder--json, whichpimdir queue canceltakes and the owner reports against. -
Added ManageSieve support behind the
sievecargo feature, on by default ([#739]).An account gains an optional
[sieve]block, andhimalaya sieveexposescapability,list,get,put,check,activate,deactivate,rename,deleteandraw.The protocol lives in the new io-managesieve library, so every SASL mechanism the other backends accept works here too, and a password or a bearer token is refused over an unencrypted connection unless
sieve.allow-cleartext-authsays otherwise.A bare
sieve.serverauthority takessieve://with STARTTLS on, where theimapandsmtpones take their implicit-TLS scheme: RFC 5804 registers one port, 4190, and reaches TLS on it through STARTTLS.sieves://is accepted for the deployments listening for a handshake straight away, andunix://for a local pre-authenticated proxy. -
Added the mailbox role (
inbox,sent,drafts,trash...) tomailbox list, as a ROLE column and aroleJSON field, with themailbox.list.table.role-coloroption. [#743]JMAP reads the mailbox roles, Gmail its system labels and Graph its well-known folders, resolved in one
$batchrequest. IMAP marks the inbox alone until SPECIAL-USE is read, and Maildir, m2dir and pimdir mark none. Amailbox.alias.<role>entry overrides the role the backend reports. -
Added
imap fetch --body, fetching the whole message of every UID in the sequence set over one session (BODY.PEEK[],\Seenleft unset) ([#762]).Downloading a mailbox took one
message readper message, so one login per message, which some providers cut off after a dozen in a row. The JSONbodyfield carries the octets as standard Base64, declared as such in the JSON Schema; the plain output prints the size only. -
Added the mbox backend, behind the
mboxcargo feature (off by default), over the new io-mbox library ([#697]).An
mboxblock points at a directory of mbox files and, optionally, at the spool ($MAIL) shown asINBOX. The shared commands read and write it, andhimalaya mboxexposes the raw file operations. Any mbox file also opens by passing its absolute path as the mailbox. Writes lock the file the way MTAs and MUAs do, flags live in theStatusandX-Statusheaders mutt and Dovecot use, and an index kept under the XDG cache directory makes a listing read only what changed.
Changed
-
Saving and sending at once (
--savewith--send,message send --save,message add --send) now sends first, so a failed send leaves no copy behind. A save failing after the send reports that the message was sent. -
A pimdir account sends through the store's queue and no longer uses its
smtpsection. Behaviour change. -
-m/--mailboxresolves a role after the aliases, so-m sentreaches the sent mailbox without an alias. A role carried by several mailboxes is an error naming them. -
An omitted
-m/--mailboxfalls back to the inbox role when noinboxalias is set, instead of failing. -
message deletetakes the trash frommailbox.alias.trashfirst, then from the backend. Behaviour change: an alias set on JMAP, Gmail or Graph used to be ignored. -
The wizard no longer pre-fills
mailbox.alias.*, the backends reporting their roles themselves. -
Changed Gmail draft create/update JSON output to structured draft, message and thread IDs, with schemas for both commands. Text confirmations are unchanged.
-
Changed the default
--formatofgmail messages get,gmail drafts getandgmail threads getfromfulltometadata.None of them prints more than
metadatareturns (id, labels, snippet, headers), so their output is unchanged, and Gmail no longer sends bodies that were thrown away. Pass--format fullto get a thread's MIME payloads. -
Forwarded
vendoredto io-pimdir and turned it on by default, socargo installbuilds SQLite from source and needs none on the machine. Drop it to link the system SQLite and save about 1 MB; it also vendors OpenSSL whennative-tlsis on. The Nix builds still link the store's SQLite. -
BREAKING: a
pimdirstore written before io-pimdir 0.4 is refused, and Himalaya reads the store's typed summaries.io-replica is retired, its sync engine folded into io-pimdir, and the store format moved with it: summaries are typed rows rather than a JSON meta column, and a store from the earlier draft is refused on open, saying to delete it and let Neverest resync. There is no migration, the draft offering none.
An envelope now carries every
FromandToaddress with its display name, theIn-Reply-Tolist and whether the message holds an attachment, straight from the store, andenvelope listfollows the store's newest-first order. A queued creation is rendered from the body it pins, the queued action carrying no summary any more. -
A credential command that several blocks of one account name is now spawned once per run instead of once per block.
account checkand the wizard connection tests open IMAP, SMTP and ManageSieve in a row, each block resolving its own credential, so apassorgpgentry was unlocked as many times as it was named. They now share one resolver, so it unlocks once.Two commands count as one only where the configuration wrote them identically: a shell line and the argv spelling of it stay distinct. A run reaching a single backend is unchanged.
-
BREAKING: a
pimdirmailbox is now its collection id, verbatim, andpimdir.namespaceis removed.-m imap/INBOXis what-m INBOXused to be, and a listing showsimap/INBOXin both columns.The sync engine binds a source's collections under a namespace, and Himalaya used to strip it back off, so one mailbox answered to two spellings. The store cannot be asked where that prefix ends: a collection id is opaque to it and hierarchy is a parent link rather than a separator.
The stripping was therefore a guess at the sync engine's convention, needing a config key wherever it could not decide. The id is now the only spelling, as it already is for JMAP's opaque ids, and
mailbox.alias.inbox = "imap/INBOX"is how you stop typing it. -
Renamed the plural commands that name no vendor resource to their singular, the plural staying as a hidden alias.
imap flags,maildir messages,maildir flags,m2dir messagesandm2dir flagsbecomeimap flag,maildir message,maildir flag,m2dir messageandm2dir flag. Every old spelling keeps working, hidden from--help.A command mirroring a vendor API resource keeps that API's spelling instead, so the
gmailfamily is unchanged.The
msgraphone, singular where Graph is plural, is aligned onto Graph:mail-folder,messageandattachmentbecomemail-folders,messagesandattachments, joining thechild-foldersthat already sat under the first.Every singular spelling stays as a hidden alias, where it used to be shown beside the plural, and
completion,manualandjson-schemagain the plural aliases they lacked. -
Renamed the type
configureprints fromGeneratedConfigtoConfigureOutput, the shape the rest of the family uses.What the command prints is unchanged. Only the
titleof thehimalaya-configureJSON Schema follows the new name.
Removed
- Removed the
mboxalias of themailboxcommand:himalaya mboxnow runs the new mbox backend command. Scripts should call the command by its name,himalaya mailbox, rather than by an alias.
Fixed
-
Fixed a message sent over JMAP staying in the drafts mailbox as a draft.
The submission now asks the server to move it to the sent mailbox, unset
$draftand set$seenonce sent (RFC 8621 section 7.5onSuccessUpdateEmail). The sent mailbox is thesent-role one, or the newjmap.sent-mailbox-id; without either, the message only loses$draft. -
Fixed a successful IMAP append being reported as failed when its UID could not be recovered ([#759]).
A server may acknowledge
APPENDwithoutAPPENDUID, even with UIDPLUS, and some (QQ Exmail) also rewrite theMessage-IDthe fallback search looks for. The save now succeeds with an unknown id, andmessage add --jsoncan return"id": null. -
Fixed the "No backend matching
auto" error saying nothing of the cause ([#740]). It now names the account and either the supported backends this build compiles in or the missing<backend>block, and points at MIGRATION.md when the account still carries the v1backendtable. -
Fixed
envelope listandenvelope searchtruncating the ID column under--max-width.A long id, such as a Maildir file name, came out cut with an ellipsis and was unusable by any follow-up command. The ID column now keeps its content width, and the header, like the rows, stays on one line.
-
Fixed
--to,--ccand--bcccarrying a display name composing aTo: <Alice <alice@example.org>>no SMTP server accepts, as--fromdid before ([#727]). Each value is now parsed as an address list, so a comma inside a quoted display name ("Doe, Alice" <alice@example.org>) no longer splits it. -
Fixed
imap rawhanging until the stream timed out when the last command was not terminated by an explicit\r\n([#764]). -
Fixed IMAP searches with non-ASCII text being rejected by Gmail (io-imap#3).
Bumped io-imap to 0.7.1, whose
SEARCHcarriesCHARSET UTF-8when its criteria hold non-ASCII text, a fix lost since v1.2.0 (#635). An ASCII search sends no charset, Outlook rejecting the UTF-8 one ([#769]). -
Fixed the
Bcc:field being transmitted over SMTP, disclosing blind recipients to everyone (#747).Bumped io-smtp to 0.4, which removes it before
DATA. The--savecopy andsmtp send, whose envelope is explicit, keep it. -
Fixed a local path carrying a
~or a shell variable being read verbatim.maildir.root = "~/Mail"looked for a directory literally named~under the working directory rather than the Maildir the sample documents, andm2dir.rootand thetls.certof every backend read the same way.Every path key now expands as the configuration is read, so no reader can forget.
pimdir.rootanddownloads-diralready expanded where they were read, and only moved onto that rule. -
Fixed a
pimdir-only build failing to compile.The client-side search evaluation the backend calls was gated on the
maildirandm2dirfeatures alone, so--no-default-features --features pimdirdid not build. The default feature set was unaffected. -
Fixed
message moveandmessage deleteon IMAP servers without the MOVE extension.Without MOVE, a move is now
UID COPY,\Deletedand, with UIDPLUS,UID EXPUNGEof the copied UIDs. Without UIDPLUS the messages stay flagged\Deletedin the source. -
Preserved message MIME payloads in
gmail threads get --json, including full-format bodies and attachment metadata (#750). -
Fixed a duplicated message disappearing from the
pimdirbackend's listing.A mailbox holding one
Message-IDtwice, which a double delivery, a retried append, a restore or a copy of a sent message all produce, used to resolve to a single stored item. One copy was kept and the other recorded on it, mirrored nowhere, so it showed in no listing and could not be read.The store now gives the second copy a key of its own, so both list, each with its own public id and neither marked, which is what the mailbox holds. Whether they are duplicates worth removing is the server's state and the user's call, so nothing here merges or hides one.
Saving a locally-composed message is unchanged: an add naming an id the mailbox already holds parks in the queue instead, the store being liberal with what a sync hands it and strict with what a producer creates.
-
Fixed
completionwriting files instead of printing the completion script to the standard output ([#736]).The script has been documented as going to stdout since the command exists, but the directory it fell back on turned
himalaya completion bashinto a file-writing command printing a report.That broke every packaging helper capturing stdout, Homebrew's
generate_completions_from_executableamong them. Giving--dirkeeps writing one script per shell.manualandjson-schemafollow the same shape, so the three behave alike: they print the single page or schema they are asked for, they take their directory through--dirwhere it used to be a positional, and they accept command names to select what to generate.Restoring the documented behaviour is a regression fix rather than a breaking change, hence the patch release, and generating completions, manual pages or schemas is not a daily gesture.
-
Fixed
maildir message copyleaving an empty message behind when the process died mid-copy ([#738]).The copy wrote straight to the name the target mailbox lists, so a death inside it left a 0-byte entry
envelope listshows as an ordinary message with blank columns,account checkcalls healthy, andmessage readalone rejects.The copy is now staged and renamed, so an interruption leaves nothing behind.
-
Fixed maildir flag writes doing nothing to a message still unread in a mailbox's
newdirectory ([#637]).message read --seenandflag addreported success and left the message untouched, a Maildir name innewhaving nowhere to carry a flag. The message now moves tocurunder the same id with its flags in the name, which is the transition every Maildir client performs.