# AUIS Revival: IMAP/SMTP Mail Quickstart
This guide takes a built tree from zero to working mail:
`messages` (or `cui`) reading your real IMAP mailbox and sending through
your provider's SMTP submission server. It reflects the state of the
revival as of 2026-07-18 (milestone 3b of the AMS/IMAP project; see
`ams-IMAP-project.md` for the architecture and roadmap).
**How it works, in one paragraph.** The historical AMS delivery system
(AMDS) stays off. Instead, a sync program (`imapsync`) mirrors your IMAP
folders one-way into a local AMS message store under `~/.IMAP/<account>/`,
which `messages` and `cui` browse through the completely unmodified store
machinery — the local store *is* the cache, as in Thunderbird's model.
Outgoing mail bypasses delivery entirely: when the `smtphost` preference
is set, the send path speaks SMTP (with STARTTLS and authentication)
directly to your provider. The mirror is currently **one-way**: local
flag changes are overwritten by the server's view on the next sync, and
writeback is a later milestone.
Fastmail is the reference provider throughout; any provider offering
IMAP + SMTP submission with app passwords should work the same way.
## Prerequisites
- A built tree — see `quickstart.md` for build and X11/font setup.
The mail pieces are built by the normal `make World`/`dependInstall`;
`imapsync` installs to `build/bin/imapsync`.
- An **app password** from your provider (Fastmail: Settings → Privacy &
Security → Integrations → New app password, scope "Mail (IMAP/SMTP)").
Regular account passwords will not work with third-party clients.
- `curl` on `$PATH` — only needed for "Load remote images" (see below);
it fetches `<img src="http(s)://...">` in HTML mail. Ships with
macOS, so normally nothing to install. Without it, remote-image
loading just silently fails to fetch (no error dialog); mail with no
remote images, and `cid:`-inline images, are unaffected.
## Step 1: credentials in ~/.netrc
Create `~/.netrc` **with mode 600** — the mail code refuses to read it if
it is group- or world-readable. One stanza per server, same app password
in both:
```
machine smtp.fastmail.com login wdc@fastmail.com password <app-password>
machine imap.fastmail.com login wdc@fastmail.com password <app-password>
```
```
chmod 600 ~/.netrc
```
Note the stanzas are keyed by *server hostname*, so both are required
even though the credentials are identical.
## Step 2: preferences
Add to `~/preferences`:
```
*.smtphost: smtp.fastmail.com
```
This one preference is the master switch for SMTP sending — with it set,
every AMS client's send path goes to that server instead of the
historical delivery system. It also disables the (obsolete, and on
modern resolvers unreliable) client-side DNS validation of destination
hosts; set `*.validatedesthosts: 1` if you ever want that back.
Do **not** add the `mspath` line yet — the store refuses unknown path
elements, so the mirror directory must exist first (step 4).
## Step 3: identity — the From address
AMS stamps every outgoing message's `From:` header itself (any From you
type is deleted and replaced), as `<login>@<MyMailDomain>` — and the
domain comes from the `ThisDomain` key of the **AndrewSetup**
configuration file, falling back to your machine's hostname if no such
file exists. An unset ThisDomain therefore produces
`you@your-machine.lan`, which your provider will refuse to relay to
external addresses (Fastmail: `551 5.7.1 Not authorised to send from
this header address`).
The AndrewSetup search path ends at `${ANDREWDIR}/etc/AndrewSetup`,
which the revival's `site.h` points at the build tree — so no root
access is needed. Create `build/etc/AndrewSetup` containing:
```
ThisDomain: fastmail.com
AMS_OnlyMail: No
```
Your UNIX login name must match the local part of your mail address for
this to compose correctly (`wdc` + `fastmail.com` → `wdc@fastmail.com`).
The full name in the From display comes from your account's GECOS field.
`AMS_OnlyMail` matters for mirrored folders specifically — see step 6.
**`AndrewSetup` is hand-authored, not a build artifact** — no
Imakefile installs or regenerates it, and it lives inside `build/`, so
`make Clean` (or any other wipe of `build/`) deletes it silently along
with everything else. Run `revival/tools/write-andrewsetup` any time
after a clean to recreate it with these settings (it refuses to
clobber a file that's already there; pass `-f` to overwrite). See
"AndrewSetup settings" in `quickstart.md` for the full reference on
what's in it and why.
## Step 4: first mirror
```
build/bin/imapsync -v
```
Defaults: account root `~/.IMAP/fastmail`, folder `INBOX`, credentials
via the `imap.fastmail.com` netrc stanza. The first run fetches every
message (a few minutes for a few thousand messages); subsequent runs are
incremental and near-instant, so re-run it whenever you want new mail.
There is no daemon mode yet — cron or a by-hand run is the current
answer.
Options:
- more folders: `-folders INBOX,Archive,Sent` on the command line, or
the `imapsyncfolders` preference (comma-separated IMAP names)
- `-full-check`: additionally detect messages expunged on the server
and mark the local copies deleted (never purges them); costs a full
mailbox scan, so it is opt-in
- `-root <dir>`: mirror somewhere else (used by the test suites)
**There is no folder auto-discovery.** `imapsync` only ever mirrors
folders you name explicitly — there is no "sync everything on the
account" mode, and nothing watches the server for folders that didn't
exist yet at your last `-folders`/`imapsyncfolders` edit. A folder
created after the fact (via Thunderbird, a filter, webmail, anything
other than this tool) will not appear in `messages` until you notice,
add its name to the list, and re-run `imapsync`. If you have many
folders and want to enumerate what actually exists on the server
before building that list, `imaptest.test` (built alongside `imapsync`,
in `src/overhead/mail/lib`) has a read-only `list` subcommand:
```
src/overhead/mail/lib/imaptest.test list imap.fastmail.com 993 ~/.netrc imap.fastmail.com
```
prints every real folder name via a plain IMAP `LIST "" "*"` — a
one-time (or occasional, if you add folders later) way to build or
refresh your `imapsyncfolders` value, not something `imapsync` does
for you automatically.
Through Milestone 3 the sync was strictly read-only on the IMAP side.
**As of Milestone 4 (writeback, 2026-07-23), that's no longer true**:
if a mirrored folder has a `.MS_Journal` (local mutations captured via
`messages`/`cui` — mark read, delete, purge, copy/compose in), the very
next `imapsync` run on that folder replays those changes to the server
for real (`STORE`/`EXPUNGE`/`APPEND`). Server-observed changes always
win over local ones. Only mirror folders you're prepared to have
written back to; see `revival/doc/ams-IMAP-project.md` §7 Milestone 4
and `revival/doc/claude-history/imap-writeback-REPORT.md` for the full
design and a real incident this caused during development (a
now-fixed, unrelated test-suite bug — not a defect in `imapsync`
itself).
## Step 5: point mspath at the mirror
Now add to `~/preferences` (colon-joined with whatever your mspath
already has; if you have no mspath line, this is the whole thing):
```
mspath: $default:~/.IMAP/fastmail/.MESSAGES
```
The `.MESSAGES`-suffixed root is required — that literal path component
is how the store recognizes a message-directory tree (it is the same
convention as your personal `~/.MESSAGES`). An optional label prefix,
`$default:[fastmail]~/.IMAP/fastmail/.MESSAGES`, names the tree in
folder listings.
## Step 6: browse
```
cd build && bin/messages
```
The mirrored INBOX needs two things to appear in the default startup
folder view ("Expose New"), rather than requiring **Message Folders →
Expose All** every time:
1. `AMS_OnlyMail: No` in `AndrewSetup` (step 3) — without it, Expose
New is hard-restricted to `$HOME/.MESSAGES` and excludes every other
mspath root outright, before subscription status is even checked.
2. The folder subscribed at **Ask** or **Show All**, not plain
Subscribe — Expose New additionally filters on "has new mail since
last read," and only Ask/Show All bypass that check unconditionally.
A plain-subscribed folder only shows up in Expose New when it
genuinely has unread mail newer than the last time you looked at
it, same as any classic AMS folder.
Select the folder (once visible via Expose All) → Message Folders →
Alter Subscription → Show All to get the always-visible behavior.
Captions, bodies, and seen/unseen state all come from the mirror;
re-run `imapsync` to bring in new mail and updated flags.
**Many menu items are grayed out until you enable them in Set
Options** (Other menu → Set Options) — this is not folder- or
message-specific, it's a global, off-by-default toggle per feature, a
classic AUIS "experience level" progressive-disclosure design. Two
you'll likely want early: **"File into... menus"** (`EXP_FILEINTOMENU`
— without it, every "File Into" submenu item stays disabled no matter
what's selected or displayed) and **"Mark as Unseen menu"**
(`EXP_MARKASUNREAD` — same story for "This Message → Mark as Unread").
If a menu item looks permanently disabled regardless of what you click
on or select, suspect a Set Options toggle before suspecting a bug.
**Mail headers have proliferated a lot since 1988.** A real message
routed through a modern provider commonly carries 40-80 header lines —
`Received:` hops, `DKIM-Signature`/`ARC-*`/`Authentication-Results`,
`X-Microsoft-*`/`X-Google-*` diagnostics, spam-score headers, and more
— where AMS's original design assumed a handful. Both `messages` and
`cui` show every one of these by default, unfiltered; neither app was
changed to filter them by default (a deliberate choice — see the
"AMS-over-IMAP" project entry in `roadmap.md`). In `messages` this is
mostly a non-issue in practice: the header block is inserted in a tiny
font ahead of the body, and the view scrolls to land on the actual
message content, not the top of the header pile. `cui`, reading in a
plain terminal, has no equivalent — `type` prints every header line
top to bottom, and you page through all of them (`-- More --`) before
reaching the body. To quiet that down in `cui`:
```
set headers
keep subject from to date
```
`set headers` turns on header *filtering* (off by default, somewhat
confusingly — "off" means "show everything unfiltered," not "hide
headers"); `keep` then whitelists just the header names you listed
("omit" does the opposite: hide *only* the header names you list,
otherwise print everything else). Both are `LEVEL_EXPERT` commands, so
`cui` will ask you to confirm running one at the default novice level
the first time. To make this permanent, put both lines in `~/.cuirc`
— `cui` sources that file automatically on startup (silently skipped
if absent), the same mechanism the `source` command uses interactively.
## Reading HTML and image-rich mail
Most real-world mail today is HTML, often `multipart/alternative` with
a plain-text part alongside it. `messages` renders the HTML part
natively — as styled ATK text (fonts, colors, tables, inline images,
underlined links), not raw markup and not shelled out to metamail — the
same MIME part-selection logic that already picked the right part for
plain-text mail just picks HTML now too. `cui`, having no styled-text
display at all, renders the HTML part down to plain text instead (tags
stripped, structure kept as best it can).
**Prefer plain text over HTML** (Set Options): when a message offers
both, always show the plain-text part instead of the styled HTML one.
Shared with `cui` via the same `ams.preferplaintext` preference, though
`cui` only picks up a change the next time it starts.
**Remote images are off by default.** HTML mail routinely references
images by URL instead of attaching them, and the mere act of fetching
one tells the sender your address is live and that you opened that
specific message — so `messages` shows a placeholder rather than
fetching automatically. Three settings control this (all in Set
Options unless noted):
- **Load remote images in HTML mail** — off by default; turn on to
fetch and display remote images for every message, from anyone, from
now on. **This Message → Load Remote Images** does the same for just
the one currently-displayed message, just this once, without
changing the setting above.
- **Trusted senders (always load their images)** — a comma-separated
list of addresses or domains whose images load automatically even
while the switch above stays off. The easiest way to add one is
**This Message → Add Sender To Image Allow-list** on a displayed
message, rather than typing addresses in by hand. Independent of the
tracking-pixel check below: a trusted sender's real photos and logos
load, but an obvious tracking pixel from that same sender is still
blocked unless that check is also off.
- **Block obvious tracking-pixel images** — on by default; recognizes
the common shapes of an invisible tracking pixel (a declared 0×0/1×1
size, or a URL containing a word like "beacon" or "track") and skips
fetching them, rather than fetching and then hiding the result — the
fetch itself is what tips off the sender. Best-effort, not a
guarantee: some trackers use ordinary dimensions or an unremarkable
URL and still get through once remote images are being loaded at
all.
**This Message → Show as Plain Text** re-renders the current message
with tags stripped instead of the styled renderer — an escape hatch for
the rare HTML message whose table layout renders badly.
## Step 7: send
Compose and send from `messages` or `cui` as normal. With `smtphost`
set, "Your message has been sent" means the provider's submission
server accepted it.
**Plain messages go out as plain text automatically.** A message with
no formatting is silently stripped and sent as ordinary text that any
mail client can read. (If your sends arrive as an empty body with a
small attachment of ATK markup, you are running a binary from before
2026-07-18 — that was a 64-bit porting bug in
`MS_GetConfigurationParameters` that made clients believe an AMS
delivery system would down-convert for them; rebuild.)
**For messages with formatting** (fonts, insets), messages asks per
send: remove the formatting, send Andrew format, or send MIME. Set
```
mailsendingformat: mime
```
in `~/preferences` to pre-answer the Andrew-vs-MIME half of that
question — MIME sends multipart/alternative with a plain-text first
part, so non-ATK readers still get something readable. Recognized
values: `mime`, `atk`, `ask` (the default).
If you want explicit per-message **Send Formatted / Send Unformatted**
menu items, enable "Send Formatted/Unformatted menus" in Set Options.
There is no hand-editable preference line for option checkboxes:
messages persists them as an opaque hex bitmask
(`messages.BinaryOptions` in `~/preferences`, written by the Set
Options interface), so the GUI is the supported way to set them.
A good first test is a message to yourself, then `imapsync -v` and
watch it appear in the mirrored INBOX.
## Current limitations
| Limitation | Status |
|---|---|
| One-way sync: local flag changes revert to the server's view on next sync; don't file into or delete from mirror folders | Writeback is milestone 4 |
| App passwords only | XOAUTH2 is milestone 5 |
| Default folder-view visibility needs `AMS_OnlyMail: No` (AndrewSetup) plus Ask/Show-All subscription | M3c, resolved 2026-07-22 — see step 6 |
| Generic (non-HTML, non-inline-image) MIME attachments still shell out to metamail, which does nothing on this platform | Pre-existing gap, separate from the IMAP project — HTML mail and inline gif/jpeg/png images render natively instead, see "Reading HTML and image-rich mail" above |
| `cid:`-referenced inline images inside HTML mail (as opposed to `http(s)://` ones) show a placeholder, not the image | `mimepart.c` doesn't parse Content-ID yet |
| No sync daemon | Re-run `imapsync` by hand or from cron |
| No folder auto-discovery — new server-side folders don't appear until named and synced | Use `imaptest.test list ...` to enumerate real folder names; see step 4 |
## Troubleshooting
- **`551 5.7.1 Not authorised to send from this header address`** —
your From domain is wrong; see step 3.
- **`The mspath element ... does not exist`** — run `imapsync` before
adding the mspath element, and check the path ends in `.MESSAGES`.
- **netrc silently not used / auth failures** — check `~/.netrc` is
mode 600 and has a stanza for the exact server hostname being
contacted (SMTP and IMAP stanzas are separate).
- **Seeing exactly what is said to the server** — set `AMS_SMTP_TRACE=1`
in the environment before launching `messages` or `cui` and the whole
SMTP dialogue (EHLO, MAIL FROM, each RCPT reply, the final verdict) is
printed to stderr, with the AUTH credential line redacted and the
message body omitted. This is the fastest way to see which identity
the server is objecting to when it refuses a send.
- **`Can't write message to /usr/lib/sendmail`** — the send path
believed `smtphost` was unset and fell back to the (nonexistent)
sendmail binary. If your preferences file plainly has the line, the
process's preference reads have gone bad; quit and restart the
application (a failed preference load is cached for the life of the
process) and retry.
- **Sends work to your own address but not externally** — that is the
step-3 From-domain problem again; external relay is where providers
enforce the header From.