BOWER(1)
πΈ bower(1) β the manual, but make it group chat πΈ
okay so bestie, first things first: bower is a curses frontend for the Notmuch email system (http://notmuchmail.org/). The author literally wrote it for themselves, but you might like it too. π
bower is written in Mercury (http://mercurylang.org/).
see some screen shots β screenshots β¨
π«Ά Requirements π«Ά
bower makes use of standard Linux utilities, so it will likely require some work to get it running on other systems. ngl, plan for that.
At compile time, you're gonna need:
- Mercury compiler (20.06 or later, including
release-of-the-day); currently available athttp://dl.mercurylang.org/index.html - ncurses with wide character support
- gpgme (GnuPG Made Easy)
At run time, you need:
- notmuch
- GNU coreutils:
base64 - an SMTP client to send messages (configurable)
file(1)to detect MIME types when adding attachments
And to create a man page from this readme, you will need:
- pandoc (
https://github.com/jgm/pandoc) β universal document converter awk
π οΈ Compiling π οΈ
so first up: to install Mercury from source you can follow these steps:
tar xf mercury-srcdist-VERSION.tar.gz
cd mercury-srcdist-VERSION
./configure --prefix=/path/to/mercury
make install PARALLEL=-j6 LIBGRADES=
export PATH=$PATH:/path/to/mercury/bin
where PARALLEL=-j6 reflects the number of parallel jobs to use. parallel girlbossing, honestly. π
With Mercury installed and mmc in your PATH, run:
make
On Gentoo, and perhaps some other Linux distributions, run:
make WITH_NCURSESW_DIR=1
You may want to edit src/Mercury.options to suit your system. On success, the build will produce a binary named bower. slay. β¨
To generate a man page, run:
make man
This will produce bower.1 for installation into your man search path.
Packages are also available in Alpine Linux (https://pkgs.alpinelinux.org/package/edge/community/x86_64/bower), AUR (https://aur.archlinux.org/packages/?K=bower), Gentoo (https://packages.gentoo.org/packages/mail-client/bower), GuixRUS (https://git.sr.ht/~whereiseveryone/guixrus), and Nixpkgs (https://search.nixos.org/packages?query=notmuch%2Dbower). pick your fighter π
π Configuration π
You can configure bower by creating a file in one of these locations, in order of priority:
- the path specified in the environment variable
BOWER_CONFIG(if set) - the path
$XDG_CONFIG_HOME/bower/bower.conf(if the environment variableXDG_CONFIG_HOMEis set and non-empty) - the path
$HOME/.config/bower/bower.conf(if the environment variableHOMEis set) - a path
$basedir/bower/bower.confwhere$basediris an element of the colon-separated list given in the environment variableXDG_CONFIG_DIRS(defaults to/etc/xdg)
The details of the configuration file are given in bower.conf.sample. read it, it's giving documentation π
Bower is designed such that it can be run on the local machine but call out to notmuch on a remote machine (that holds your mail) via ssh. The advantage is that you can start helper programs on the local machine (e.g. a web browser or image viewer), and add or save attachments on the local filesystem, even if your mail archive is stored on a different machine. literally the best of both worlds π
Bower also keeps some centralised information in the notmuch configuration file. You may wish to tell bower about your Maildir hierarchy, namely where to place draft and sent messages. The defaults are given here:
[bower:maildir]
drafts_folder = Drafts
sent_folder = Sent
It is recommended that your .notmuch-config file contains at least these search exclusions:
[search]
exclude_tags=deleted;draft
π¦ Usage π¦
bower [OPTION]... [SEARCH-TERM]...
bower [OPTION]... mailto:MAILTO
Run bower to start, optionally passing a list of initial search terms. If no search terms are provided, bower will use the search string ~default, where ~default is a search term alias (see below). By default, ~default is equivalent to searching for messages from the last week, but you are free to change it. your inbox, your rules π«Ά
Running bower with a single mailto: argument will go directly to the compose screen. straight to business.
The only options supported so far are -h, --help and --version. that's it, that's the whole options section, periodt.
π Views π
There are two main views: the index, and the combined thread/pager. iconic duo.
β¨οΈ Index view keys β¨οΈ
This view shows the notmuch search results for the current query. The keys are:
j,kβ next, previous threadg,Gβ first, last thread[,]β scroll half page up, downTab,,β go to next unread threadEnterβ open threadAlt+Enterβ open thread in obscured model,~β change search terms ("limit")Alt+Key β invoke search term alias named by Key=β refresh search resultszβ toggle authors column
then the search-inside-your-results squad:
/,?β search for string within resultsnβ skip to next search resultNβ skip to next search result in opposite direction
now the tagging block, aka the main character energy:
Uβ toggleunreadtag on current threadFβ toggleflaggedtag on current threadaβ toggleinbox,unreadtags on current thread (archive)dβ setdeletedtag on current threaduβ unsetdeletedtag on current thread$β apply+spam -unreadtags on current thread+,-β add/remove arbitrary tagstβ select/unselect threadCtrl+Aβ select all threadsTβ unselect all threads'β bulk tag changes on selected threads"β same as'but retain selections afterwards
mail stuff:
mβ compose new messagerβ reply to sendereβ reply to everyoneLβ reply to listRβ recall postponed message@β add to address book
and the rest:
|β pipe thread IDs to commandqβ quit
Pressing Alt+Key will treat Key as a single character search alias that is invoked directly without further prompting (see below for search term aliases). This provides a convenient way to switch between frequent queries. lowkey the best shortcut in here π₯
π Thread/pager view keys π
This view pages through an entire thread. The keys are:
j,kβ next, previous messageg,Gβ first, last messageb,spaceβ previous, next page\,Enterβ previous, next line[,]β previous, next half pageTab,,β go to next unread messagepβ go to parent messageSβ skip quoted textOβ toggle ordering of messages (threaded or flat)Mβ toggle obscuring of unmatched messages=β refresh search results
search squad again:
/,?β search for stringnβ skip to next search resultNβ skip to next search result in opposite direction
reading + tagging:
Jβ mark current message read and go to next messageKβ mark current message read and go to previous messageUβ toggleunreadtag on current message^Rβ removeunreadtag on preceding messagesFβ toggleflaggedtag on current messageaβ toggleinbox,unreadtags on current message (archive)dβ adddeletedtag on current messageuβ removedeletedtag on current message$β apply+spam -unreadtags on current message+,-β add/remove arbitrary tagstβ select/unselect messageCtrl+Aβ select all messagesTβ unselect all messages'β bulk tag changes on selected threads"β same as'but retain selections afterwards
mail again:
rβ reply to sendereβ reply to everyoneLβ reply to listWβ forward messageBβ resend message to another address ("bounce")Eβ use current message as a template for a new messageRβ recall postponed message@β add to address book
parts and attachments and such:
vβ highlight next visible attachment or URL or folded textVβ highlight next visible attachment or top of messages,wβ save highlighted message/part to fileoβ open highlighted message/part/URL with external programzβ cycle alternative parts / toggle inline display / decryptZβ toggle inline display of part / decryptz,Zβ expand/collapse folded textyβ verify signed part
piping and leaving:
|β pipe thread or message IDs to commandi,qβ return to indexIβ return to index, removingunreadtag on all messagesAβ return to index, removinginboxandunreadon messages
The 'o' command, which opens parts and URLs, takes a command using Unix shell quoting syntax. If the command ends with an unquoted '&' character then the command will be run in the background. yes, it's giving shell, because it is shell π
Note: forward (W) and edit-as-new (E) currently will not create a proper message template for messages that are manually decrypted in the thread view. A workaround is to setcrypto.decrypt_by_default = truein the bower configuration so that encrypted messages are automatically decrypted upon opening a thread. you're welcome, iykyk π
In "obscured" mode, messages unmatched by the search query in the index view will be displayed in a different colour, and ignored by navigation keys (e.g. j, k, and string search). The mode can be toggled with the 'M' key.
π Limit command syntax extensions π
The l (limit) command in the index view slightly extends the notmuch search term syntax with these macros:
~Aβ disable default cap on number of search results~Fβtag:flagged~Uβtag:unread~Dβtag:deleted~d DATE..DATE~d DATE..~d ..DATE~d DATE
By default, the number of threads displayed in the index view is capped at 300 threads (configurable). Add ~A to the search string to get all results. no cap, literally π§’
The ~d syntax passes dates through to notmuch as a "date:" range; see notmuch-search-terms(7) for the date range syntax. You may use curly brackets to surround date strings containing spaces. The single date form ~d DATE is equivalent to ~d DATE..DATE.
Some examples:
~d 2011-06-01..
~d {2 weeks ago}..{last week}
~d yesterday
Search term aliases ππ
Okay so besides the built-in macros, bower tries to expand tokens shaped like ~NAME in two ways:
- if thereβs a search term alias called
NAME, then the token gets replaced with its expansion - if thereβs a notmuch named query called
NAME, then the token gets replaced withquery:NAME
Search term aliases are defined in the notmuch config file in a section called [bower:search_alias]. Expansions can use other expansions, but non-recursive only, bestie. Example:
[bower:search_alias]
default = ~lw
lw = ~d {last week}..
2w = ~d {2 weeks ago}..
lm = ~d {last month}..
ly = ~d {last year}..
notmuch = to:notmuch
bower = ~notmuch AND bower
bower_recent = ~lm ~bower
As mentioned earlier, the ~default alias sets the initial search query if you run bower without command-line arguments. π
The differences between search term aliases and notmuch named queries are:
- search term aliases are specific to bower
- search term aliases may use bower-specific syntax, and refer to other search term aliases
- named queries are stored in the notmuch database instead of the notmuch config file
You can tab complete search term aliases and notmuch named queries. β¨
Tag modification behaviour π·οΈπ
Tag modifications in the index view are (mostly) performed asynchronously to minimise stutter. Remember to quit properly using q to ensure all tag modifications are completed before exiting. Tag modifications will be retried a limited number of times on failure, e.g. because the notmuch database is locked (this was a problem with old notmuch versions).
Unlike the index view, tag modifications in the thread/pager view will only be applied upon returning to the index view. If bower is terminated before returning to the index view then any tag modifications made in the thread view will be lost. π So likeβ¦ donβt ghost the index view, okay?
Polling for new messages ππ¦
By default, bower will call notmuch count every 60 seconds in both the index and thread views in order to notify you of new unread messages matching the search terms in the index, or new messages in the current thread. You can change the polling frequency or disable it with the configuration option ui.poll_period_secs.
You can also configure a command to run when new messages are found using the command.poll_notify option. π
Simple address book ππ
Bower can use the notmuch address command to look up email addresses where required, e.g. when composing a new message. Only addresses that appear in the From header of messages from the last year will be considered; this restriction improves performance and avoids finding stale email addresses.
Address aliases can also be kept in the notmuch config file in a section called [bower:addressbook]. For example:
[bower:addressbook]
someone = Someone <someone@example.org>
someoneelse = someoneelse@example.org
You can add to the address book using @ in the index or thread views. π«Ά
Tab completion will prefer address aliases over addresses found by notmuch address.
Sending mail π€π
You can send mail through one or more accounts defined in bower.conf. An account is selected by matching the From address on the message to the address on the account. Bower performs two steps when sending a message:
- Run the configured
sendmailcommand with the message on standard input. The command should pass the message onto an SMTP server. If thesendmailcommand fails (exits with non-zero status) then the message is considered not sent, and the next step does not run. - Run the βpost-sendmailβ step.
The default behaviour (if post_sendmail is not set) is to use notmuch insert to add the message to the mail store, and to the database with the sent tag and without the unread tag.
Otherwise, if post_sendmail is set to a non-empty command then the command is run with the message on standard input.
Otherwise, if post_sendmail is set to the empty string then no command is run.
If both the sendmail and post_sendmail commands will run on the same remote server then there is a slight inefficiency because a single message would need to be transferred to the remote server twice. If thatβs a concern, you could combine the two steps into a single script to be run at the sendmail step, and disable the post_sendmail command. π‘
Encryption and signing (beta) ππ§
Bower can produce encrypted and/or signed PGP/MIME messages through GnuPG. Message decryption and signature verification are performed by notmuch. It is up to the user to configure GnuPG on the machines running bower or notmuch.
Author ππ
Peter Wang novalazy@gmail.com β¨mailto:novalazy@gmail.comβ©
Feel free to contact me with feedback or suggestions. π«Ά
Byeee bestie π β and shoutout to the bower() footer at the bottom, holding the source/version and date like the tiny iconic detail it is. β¨
π¦ where this page comes from
- alpine 3.24: bower-doc 1.1.1-r1 Β· GPL-3.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms