I/O Email

Email client library, written in Rust.
This library is composed of 2 feature-gated layers:
- Low-level I/O-free coroutines: these
no_std-compatible state machines wrap the underlying io-imap, io-jmap, io-maildir, io-m2dir and io-smtp coroutines and surface a shared least-common-denominator type on completion - Mid-level std client: a standard, blocking unified client that dispatches the shared API across every registered backend
Table of contents
Features
- Shared LCD types:
Mailbox,Envelope,Address,Flagthat fit IMAP, JMAP, Maildir, m2dir and SMTP. - I/O-free coroutines:
no_stdstate machines per (backend, operation), wrapping the underlying io-* coroutine and producing a shared type on completion. - Unified std client (
clientfeature): blocking dispatcher that routes shared-API calls to the highest-priority registered backend (Maildir → m2dir → JMAP → IMAP for storage, JMAP → SMTP for send). - TLS for the network backends (gated by the same
rustls-ring/rustls-aws/native-tlsfeatures as the underlying io-* crates). - Optional search DSL (
searchfeature) and serde round-trip on every shared type (serdefeature).
[!TIP] I/O Email is written in Rust and uses cargo features to gate backend support. The default feature set is declared in Cargo.toml or on docs.rs.
Backend coverage
| Operation | IMAP | JMAP | Maildir | m2dir | SMTP |
|---|---|---|---|---|---|
list_mailboxes |
yes | yes | yes | yes | |
create_mailbox |
yes | yes | yes | yes | |
delete_mailbox |
yes | yes | yes | yes | |
diff_mailboxes |
yes | ||||
list_envelopes |
yes | yes | yes | yes | |
search_envelopes (feature search) |
yes | yes | yes | yes | |
diff_envelopes |
yes | yes | |||
watch_mailbox |
yes | yes | |||
get_message |
yes | yes | yes | yes | |
add_message |
yes | yes | yes | yes | |
copy_messages |
yes | yes | yes | yes | |
move_messages |
yes | yes | yes | yes | |
delete_message |
yes | yes | yes | yes | |
store_flags |
yes | yes | yes | yes | |
send_message |
yes | yes |
Usage
I/O Email can be consumed two ways, depending on how much of the I/O stack you want to own. Each mode is gated by cargo features.
Whichever mode you pick, every shared-API coroutine implements the backend trait of the protocol it targets (ImapCoroutine, JmapCoroutine, MaildirCoroutine, M2dirCoroutine, SmtpCoroutine). The resume(...) method returns the matching <Backend>CoroutineState<Yield, Return> with two variants:
Yielded(Y): intermediate.Yis the backend's standard<Backend>Yield(WantsRead/WantsWritefor the network backends,WantsDirRead/WantsFileCreate/WantsRenameetc. for the filesystem ones), plus a dedicatedEvent(WatchEvent)variant on watch coroutines.Complete(R): terminal. By conventionR = Result<Output, Error>carrying the operation's final value typed against the sharedMailbox/Envelope/ shared payload.
The std client owns the resume loop for you; the I/O-free mode hands it back so you can drive the same coroutine under any blocking, async, or fuzz harness.
Coroutines
No client feature required: every wrapper lives under <domain>::<protocol>::<op> (for example mailbox::imap::list::ImapMailboxList, message::jmap::add::JmapMessageAdd) and is built straight from the shared inputs. You own the loop and the syscalls; the library only produces operations and consumes their results.
Create a fresh Maildir mailbox against a blocking caller (the same shape works under async or in-memory replay):
use fs;
use MaildirMailboxCreate;
use ;
let store = MaildirStore ;
let mut coroutine = new.unwrap;
let mut arg: = None;
loop
println!;
Network backends follow the same pattern but yield WantsRead / WantsWrite(Vec<u8>) instead; see io-imap, io-jmap and io-smtp for the full TCP / TLS / authentication setup that authenticates the stream before the wrapper coroutine runs.
Std client
Enable the client feature (pulled in by every backend feature) and at least one backend. EmailClientStd::new() starts empty; with_<protocol>(client) plugs in an already-built per-protocol client, while connect_<protocol>(url, tls, ...) opens the connection through the underlying io-* crate and fills the slot in one shot.
[]
= "0.1.0"
use ;
use ;
use SecretString;
use Url;
let url = parse.unwrap;
let tls = default;
let sasl = SaslLogin ;
let mut client = new
.with_maildir
.connect_imap
.unwrap;
for mbox in client.list_mailboxes.unwrap
Dispatch priority on storage reads walks the registered backends Maildir → m2dir → JMAP → IMAP (local before network, cheap before expensive); send routes JMAP → SMTP. Pick which slots to fill based on the workload (local-first sync vs network-first transactional client).
Examples
See complete examples at ./examples.
Have also a look at real-world projects built on top of this library:
- Himalaya CLI: CLI to manage emails
- Himalaya TUI: TUI to manage emails
- Neverest: CLI to synchronize emails
AI disclosure
This project is developed with AI assistance. This section documents how, so users and downstream packagers can make informed decisions.
-
Tools: Claude Code (Anthropic), Opus 4.7, invoked locally with a persistent project-scoped memory and a small set of repo-specific rules.
-
Used for: Refactors, mechanical multi-file edits, boilerplate (feature gates, error enums, derive macros, trait impls), test scaffolding, doc polish, exploratory design conversations.
-
Not used for: Engineering, critical code, git manipulation (commit, merge, rebase…), real-world tests.
-
Verification: Every AI-assisted change is read, compiled, tested, and formatted before commit (
nix develop --command cargo check / cargo test / cargo fmt). Behavioural correctness is verified against the relevant RFC or upstream spec, not assumed from the model output. Tests are never adjusted to fit AI-generated code; the code is adjusted to fit correct behaviour. -
Limitations: AI models occasionally produce code that compiles and passes tests but is subtly wrong: off-by-one errors, missed edge cases, plausible but nonexistent APIs, stale RFC references. The verification workflow catches most of this; it does not catch all of it. Bug reports are welcome and taken seriously.
-
Last reviewed: 06/06/2026
License
This project is licensed under either of:
at your option.
Social
- Chat on Matrix
- News on Mastodon or RSS
- Mail at pimalaya.org@posteo.net
Sponsoring
Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:
- 2022 → 2023: NGI Assure
- 2023 → 2024: NGI Zero Entrust
- 2024 → 2026: NGI Zero Core
- 2027 in preparation…
If you appreciate the project, feel free to donate using one of the following providers:
