Email client library, written in Rust.
- Shared least-common-denominator types (
Mailbox,Envelope,Address,Flag) that fit IMAP, JMAP, Maildir and SMTP. - One I/O-free coroutine per backend / operation, wrapping the underlying io-imap / io-jmap / io-maildir / io-smtp state machine and producing shared types on completion.
EmailClientStd(featureclient): blocking unified client over all enabled backends.
| Operation | IMAP | JMAP | Maildir | SMTP |
|---|---|---|---|---|
list_mailboxes |
yes | yes | yes | |
list_envelopes |
yes | yes | yes | |
get_message |
yes | yes | yes | |
add_message |
yes | yes | yes | |
add_flags |
yes | yes | yes | |
set_flags |
yes | yes | yes | |
delete_flags |
yes | yes | yes | |
copy_messages |
yes | yes | yes | |
move_messages |
yes | yes | yes | |
send_message |
yes | yes |
The io-email library is written in Rust, and relies on cargo features to enable or disable functionalities. Default features can be found in the features section of the Cargo.toml, or on docs.rs.
[dependencies]
io-email = "0.0.1"List IMAP mailboxes over a blocking TCP socket:
use std::{io::{Read, Write}, net::TcpStream};
use io_email::imap::mailbox_list::*;
use io_imap::context::ImapContext;
let mut stream = TcpStream::connect("imap.example.com:143").unwrap();
let mut buf = [0u8; 16 * 1024];
let context = ImapContext::new();
let mut coroutine = ImapMailboxList::new(context);
let mut arg: Option<&[u8]> = None;
let mailboxes = loop {
match coroutine.resume(arg.take()) {
ImapMailboxListResult::Ok(mailboxes) => break mailboxes,
ImapMailboxListResult::WantsRead => {
let n = stream.read(&mut buf).unwrap();
arg = Some(&buf[..n]);
}
ImapMailboxListResult::WantsWrite(bytes) => {
stream.write_all(&bytes).unwrap();
arg = None;
}
ImapMailboxListResult::Err(err) => panic!("{err}"),
}
};
for mbox in mailboxes {
println!("{}", mbox.name);
}JMAP coroutines also surface WantsRedirect { url, .. }; Maildir coroutines use filesystem variants (WantsDirRead, WantsFileCreate, WantsRename, ...).
[dependencies]
io-email = { version = "0.0.1", features = ["client", "imap", "rustls-ring"] }use io_email::client::EmailClientStd;
use io_imap::client::ImapClientStd;
use pimalaya_stream::{sasl::SaslLogin, tls::Tls};
use secrecy::SecretString;
use url::Url;
let url = Url::parse("imaps://imap.example.com")?;
let tls = Tls::default();
let sasl = SaslLogin {
username: "alice@example.com".into(),
password: SecretString::from("hunter2".to_owned()),
};
let imap = ImapClientStd::connect(&url, &tls, false, Some(sasl))?;
let mut client = EmailClientStd::from(imap);
for mbox in client.list_mailboxes(true)? {
println!("{}: total={:?} unread={:?}", mbox.name, mbox.total, mbox.unread);
}Same call site against Maildir:
use io_email::client::EmailClientStd;
use io_maildir::client::MaildirClient;
let maildir = MaildirClient::new("/home/alice/Maildir");
let mut client = EmailClientStd::from(maildir);
for mbox in client.list_mailboxes(true)? {
println!("{}: total={:?} unread={:?}", mbox.name, mbox.total, mbox.unread);
}Have a look at projects built on top of this library:
- himalaya: CLI to manage emails
This project is licensed under either of:
at your option.
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: 31/05/2026
- Chat on Matrix
- News on Mastodon or RSS
- Mail at pimalaya.org@posteo.net
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:
