# backupmail: one CLI for IMAP and JMAP mailboxes

> A universal email backup + migration TUI for Gmail, SpaceMail, Fastmail and any IMAP server, and what it took to support JMAP alongside 40-year-old IMAP in the same tool.

By Himanshu Maurya · 2026-07-01 · CLI · protocols · 2 min
Canonical: https://codingstark.com/blog/backupmail-imap-jmap/

Email backup tools tend to assume IMAP and stop there. **backupmail** is a TUI/CLI that backs up and migrates mailboxes across Gmail, SpaceMail, generic IMAP — and **Fastmail over JMAP** — from one interface.

### IMAP and JMAP are two different worlds

IMAP is stateful and chatty: you `SELECT` a mailbox, then `FETCH` ranges, and the server holds your session. JMAP is a modern JSON-over-HTTP protocol — you `Email/query` and `Email/get` in batched method calls, stateless, with proper concurrency. Supporting both meant a transport interface that hides the difference:

```ts
interface MailBackend {
  listMailboxes(): Promise<Mailbox[]>;
  fetchMessages(box: string, onMsg: (raw: Uint8Array) => void): Promise<void>;
}
```

The IMAP backend streams `BODY[]` responses; the JMAP backend pages through `Email/query`, resolves blob ids, and downloads via the download URL from the session object. Same `.eml` output either way.

### The fiddly parts

- **Session discovery** for JMAP — you fetch the session resource first to learn the API/download URLs; nothing is hard-coded.
- **Idempotent resume** — backing up 60k messages shouldn't restart from zero if the connection drops, so progress is checkpointed per mailbox.
- **Rate/throttle** — providers will happily throttle you; the pool backs off instead of hammering.

Understanding the protocol *shape* — stateful streaming vs. batched JSON — is most of the work. Once the transport interface is right, the backup logic is boring, which is exactly what you want for something guarding your email.
