wasm-smtp
Rust crates for sending mail by SMTP from WebAssembly runtimes. The project separates the protocol implementation from the runtime-specific socket code so that the same SMTP engine can be reused on every host.
Crates
| Crate | Role | Status |
|---|---|---|
wasm-smtp |
Environment-independent SMTP state machine and parser. | Implemented |
wasm-smtp-cloudflare |
Cloudflare Workers socket adapter for wasm-smtp. |
Implemented |
wasm-smtp-tokio |
Tokio + rustls socket adapter for wasm-smtp. |
Implemented |
wasm-smtp-wasi |
WASI 0.2 sockets adapter (wasm32-wasip2). |
Implemented |
wasm-smtp-component |
WASM Component Model WIT interface (wit/smtp.wit). |
Implemented |
wasm-smtp is the foundation: it implements the SMTP state
machine, response parsing, command formatting, dot-stuffing, and error
classification, but does no I/O of its own. Each runtime gets its own
adapter crate that provides a Transport implementation.
Four adapters ship today:
wasm-smtp-cloudflare— Cloudflare Workers (WASM target).wasm-smtp-tokio— tokio-based servers (axum, actix, warp, hyper, plain tokio, …).wasm-smtp-wasi— WASI 0.2 runtimes (wasmtime, WAMR) targetingwasm32-wasip2.wasm-smtp-component— WASM Component Model WIT interface (wit/smtp.wit), enabling language-neutral bindings (TypeScript, Go, Python, C, …).
Minimum usage
From a Cloudflare Worker (the production target):
use connect_smtps;
# async
Or directly against wasm-smtp with any Transport you supply:
use ;
async
The body argument is a fully-formed RFC 5322 message: headers, a blank
line, then the body, with CRLF line endings. The library does not build
MIME, attach files, or compose multipart bodies.
Connection model
Two TLS models are supported:
- Implicit TLS on port 465 — the runtime negotiates TLS before any
SMTP byte is exchanged. Use
connect_smtps. - STARTTLS on port 587 — the connection starts plaintext and is
upgraded to TLS in-place after the SMTP greeting. Use
connect_smtp_starttls.
In both cases the TLS handshake is the responsibility of the
Transport implementation; wasm-smtp sees an opaque byte
stream and (for STARTTLS) a single upgrade_to_tls() signal.
Cargo features
wasm-smtp exposes two cargo features that allow size-sensitive
deployments (Cloudflare Workers' 3 MiB cap, in particular) to opt out
of functionality they will not use:
| Feature | Default | What it adds |
|---|---|---|
xoauth2 |
on | SmtpClient::login_xoauth2, AuthMechanism::XOAuth2 code paths, OAuth 2.0 token validation helpers |
oauthbearer |
on | SmtpClient::login_oauthbearer, AuthMechanism::OAuthBearer (RFC 7628 IETF-standard OAuth 2.0 SASL) |
pipelining |
on | Batch MAIL FROM + RCPT TO + DATA when server advertises PIPELINING (RFC 2920) |
smtputf8 |
off | SmtpClient::send_mail_smtputf8, validate_address_utf8, format_mail_from_smtputf8, capability check |
Defaults are chosen so that v0.3.x users see no behavior change on upgrade. To strip OAuth 2.0 support entirely (typical for transactional senders against a self-hosted Postfix or commercial relay using static passwords):
= { = "0.9", = false }
To opt into international addresses while keeping the OAuth 2.0 support:
= { = "0.9", = ["smtputf8"] }
The wasm-smtp-cloudflare adapter exposes a matching smtputf8
feature that pass-through-enables it on the core crate, so adapter-
only callers do not need a direct dependency on wasm-smtp to
opt in.
Acceptable use
This library must not be used to deliver unsolicited bulk mail, to
impersonate other senders, or to deliver mail that violates the
operating policy of any SMTP server. See TERMS_OF_USE.md.
Documentation
Long-form documentation lives in docs/src. The mdBook structure
covers project architecture, the SMTP protocol surface, the error
taxonomy, and end-to-end usage.