Table of Contents
Emanix ships no mail client. What it ships is the seam a mail client plugs
into: an Emacs that is already the desktop, a secret store for the credentials,
and the consumer's own bin directory for helper scripts.
So this page documents a shape rather than a distribution feature. The account,
the provider, the folders and the key binding all belong to the consuming flake.
The shape described here is the one this deployment runs — a local Maildir
filled by mbsync, indexed by mu, read in Emacs through mu4e.
1. Read mail
C-c M opens mu4e. The distribution binds nothing for mail; that binding is
the consumer's, as is the package.
| Key | Does |
|---|---|
RET |
Open the message |
j |
Jump to a maildir |
C-c C-u |
Refresh — sync, then reindex |
U |
Mark for refiling (archive) |
x |
Execute the marks |
q |
Quit |
Sync is manual on purpose: nothing polls a mail server in the background.
2. Three programs, and which one is broken
mbsync(fromisync) copies IMAP into a local Maildirmuindexes that Maildir into a databasemu4equeries the index and shows the result
mu4e is only the frontend. When mail looks stale or missing, the useful
question is which of the first two stages stopped — and C-c C-u runs both,
which is why it is the answer to most of this page.
The maildir root is set once, outside Emacs, and never in elisp:
mu init --maildir=~/.mail --my-address=<address>
mu index
Folder names in the configuration are relative to that root — /<account>/Sent
and the like. There is deliberately no maildir setq in the elisp: in mu4e 1.14
mu4e-root-maildir is a function that asks mu where the maildir is, and plain
mu4e-maildir is not a variable at all. Setting either creates a global that
nothing reads, while looking exactly like configuration.
2.1. The daily surface is bookmarks
The default view is a list of saved queries rather than one inbox: unread everything, today, this week, and the inbox proper. They are plain mu4e bookmarks, so editing them is editing a list in the consumer's config — the distribution has no opinion about what a mail view should be.
3. Credentials come from the secret store
Both halves read agenix secrets at run time instead of files in the home directory:
- IMAP —
mbsync'sPassCmdreads/run/agenix/mbsync-pass - SMTP —
auth-sourcesis set to/run/agenix/authinfo
The second is worth spelling out. auth-sources' default is ~/.authinfo,
which does not exist here, and the failure is silent: Emacs prompts for a
password instead of reporting a missing file. Naming the decrypted path is what
makes that failure loud. See Secrets.
Two consequences follow from the store being regenerated at activation. Editing
anything under /run/agenix does nothing. And adding an account means editing
the plaintext, re-encrypting, and rebuilding.
PassCmd, never Pass. The sync configuration is store-managed and the store
is world-readable, so a literal password in it would be readable by every user on
the machine. Reading the secret at run time is what makes the file safe to keep
in the flake at all.
4. Two traps
- Port 465 is implicit TLS.
smtpmail-stream-typemust be'tls, not'starttls— 465 wraps the connection from the first byte. A comment in this deployment claimed STARTTLS for a while while the code was correct, which is the kind of discrepancy that costs an afternoon. - The sync channel needs
Patterns *. Without itmbsyncsyncs the inbox only, while the frontend refers to Sent, Drafts, Archive and Trash. Everything looks fine until you go looking for a message you sent.
A third one is worth knowing if you pick bindings: C-c M is capital for a
reason. C-c m is a different command map, and taking it would have quietly
removed the commands living under it.
5. On a new machine
Install the stack, run mu init and mu index once, then C-c M. The index
lives under ~/.cache/mu — runtime state, not in the flake, and not recreated
by a rebuild.
In the consuming flake the stack is installed for graphical hosts only, while
the elisp is guarded so the same config still loads where mu4e is absent:
(condition-case mu4e-err
(when (require 'mu4e nil :no-error)
...)
(error (message "mu4e block skipped (%S)" mu4e-err)))
That guard is not decoration. The block signalled once, on the first host that
actually had mu4e installed, and because the whole personal config is wrapped
in one condition-case, every form after it was silently discarded — taking
unrelated settings down with it. A mail client that fails to load must not be
able to break the editor.
6. Where each piece lives
- The elisp, the sync config, the account and the binding — the consuming
flake. The sync file is linked from the flake into
~/.mbsyncrc, because that is the pathmbsyncreads. - The
mu4epackage — the consumer's, built for the sameemacs-pgtkthe desktop runs. The distribution does not ship it. - The credentials — the distribution's secret store, described in Secrets.
None of it is distribution policy. A consumer who prefers another frontend,
another sync tool or another provider keeps the rest of the seam — the store, the
Emacs, the bin directory — unchanged.