backupmail: one CLI for IMAP and JMAP mailboxes
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:
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.