I/O-free JMAP client library written in Rust, based on io-http
This library implements JMAP as I/O-agnostic coroutines — no sockets, no async runtime, no std required by the protocol layer.
| RFC | What it covers |
|---|---|
| 8620 | JMAP core — session discovery, API requests, Foo/get, Foo/set, Foo/query, Foo/changes, blobs |
| 8621 | JMAP for Mail — Mailbox, Email, Thread, Identity, EmailSubmission, VacationResponse |
use std::{net::TcpStream, sync::Arc};
use io_jmap::rfc8620::session_get::{JmapSessionGet, JmapSessionGetResult};
use io_socket::runtimes::std_stream::handle;
use rustls::{ClientConfig, ClientConnection, StreamOwned};
use rustls_platform_verifier::ConfigVerifierExt;
use secrecy::SecretString;
use url::Url;
let http_auth = SecretString::from("Bearer your-token-here");
let base_url = Url::parse("https://api.fastmail.com/jmap/session/").unwrap();
let config = ClientConfig::with_platform_verifier().unwrap();
let server_name = base_url.host_str().unwrap().to_string().try_into().unwrap();
let conn = ClientConnection::new(Arc::new(config), server_name).unwrap();
let tcp = TcpStream::connect((base_url.host_str().unwrap(), 443)).unwrap();
let mut stream = StreamOwned::new(conn, tcp);
let mut coroutine = JmapSessionGet::new(&http_auth, &base_url);
let mut arg = None;
let session = loop {
match coroutine.resume(arg.take()) {
JmapSessionGetResult::Ok { session, .. } => break session,
JmapSessionGetResult::Io { input } => arg = Some(handle(&mut stream, input).unwrap()),
JmapSessionGetResult::Redirect { url, .. } => {
// reconnect to the new URL and retry
todo!("reconnect to {url}")
}
JmapSessionGetResult::Err { err } => panic!("{err}"),
}
};
println!("Logged in as: {}", session.username);
println!("API URL: {}", session.api_url);use std::{net::TcpStream, sync::Arc};
use io_jmap::rfc8621::mailbox_query::{JmapMailboxQuery, JmapMailboxQueryResult};
use io_socket::runtimes::std_stream::handle;
use rustls::{ClientConfig, ClientConnection, StreamOwned};
use rustls_platform_verifier::ConfigVerifierExt;
use secrecy::SecretString;
// (session obtained from JmapSessionGet above)
let config = ClientConfig::with_platform_verifier().unwrap();
let server_name = session.api_url.host_str().unwrap().to_string().try_into().unwrap();
let conn = ClientConnection::new(Arc::new(config), server_name).unwrap();
let tcp = TcpStream::connect((session.api_url.host_str().unwrap(), 443)).unwrap();
let mut stream = StreamOwned::new(conn, tcp);
let mut coroutine = JmapMailboxQuery::new(&session, &http_auth, None, None, None, None, None).unwrap();
let mut arg = None;
let mailboxes = loop {
match coroutine.resume(arg.take()) {
JmapMailboxQueryResult::Ok { mailboxes, .. } => break mailboxes,
JmapMailboxQueryResult::Io { input } => arg = Some(handle(&mut stream, input).unwrap()),
JmapMailboxQueryResult::Err { err } => panic!("{err}"),
}
};
for mailbox in &mailboxes {
println!("{:?} — {:?}", mailbox.role, mailbox.name);
}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.
- 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: NGI Assure
- 2023: NGI Zero Entrust
- 2024: NGI Zero Core (still ongoing in 2026)
If you appreciate the project, feel free to donate using one of the following providers:
