A Tokio-native SMTP capture engine for local development, integration tests, and CI.
postcrate-core is the engine that powers the Postcrate desktop app — a self-contained Rust library that listens for SMTP, parses incoming mail, stores it in SQLite, and exposes everything over a small HTTP API. No Tauri, no UI, no third-party service. Embed it in your own binary, run it as a daemon, or drop it into a CI job.
Pre-1.0. The public API may change between 0.x minor versions until 1.0 lands. Already used in production by the Postcrate desktop app.
[dependencies]
postcrate-core = "0.1"use std::sync::Arc;
use postcrate_core::{CoreConfig, LogSink, Service};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cfg = CoreConfig::for_data_dir("/tmp/postcrate")?;
let service = Service::build(cfg, Arc::new(LogSink)).await?;
service.start_all().await?;
// SMTP listeners are accepting mail; the HTTP API is serving under /api/v1.
tokio::signal::ctrl_c().await?;
service.stop_all().await?;
Ok(())
}Subscribe to live events by passing a custom EventSink implementation in place of LogSink.
- SMTP server —
EHLO/HELO,MAIL FROM/RCPT TO,DATAwith dot-stuffing and RFC 5321 line limits,RSET,NOOP,QUIT,VRFY,HELP. AdvertisesPIPELINING,SIZE,8BITMIME,SMTPUTF8,ENHANCEDSTATUSCODES,AUTH PLAIN LOGIN. OptionalSTARTTLSand implicit-TLS (RFC 8314) under thetlsfeature. - Multi-mailbox lifecycle — primary, shared, and ephemeral mailboxes with TTL-based auto-expiry. Per-mailbox port allocation and live config (chaos, bounce rules, tags, pins).
- MIME parsing — built on
mail-parser. UTF-8 headers, RFC 2047 encoded-words, multipart trees, inline images viacid:references, attachments withfilename*encoding. - SQLite persistence —
sqlxwith WAL, foreign keys on, FTS5 full-text search across subject, sender, recipients, and body. - HTTP API — small Axum router under
/api/v1. JSON in/out, optional bearer-token auth, optional HTTPS, server-sent events for real-time UI hooks. - Chaos mode — deterministic-seeded SMTP failure injection (4xx, 5xx, mid-
DATAdrops, response jitter). - Bounce rules — glob-pattern address matching that responds with custom SMTP codes at
RCPT TO. - Webhooks & forwarding — POST to a downstream URL on every captured email, or relay it to a real SMTP server with an optional recipient allow-list.
- Scenario checks — link extraction, spam heuristics, SPF/DKIM/DMARC inspection, RFC 2369 / 8058
List-Unsubscribevalidation, accessibility lint, 7-client rendering profiles with fidelity badges. - Mailtrap-compatible alias — a subset of the Mailtrap REST surface is exposed at
/api/accounts/…so existing test suites can drop in without code changes.
| Feature | Effect |
|---|---|
tls |
Enables STARTTLS and implicit-TLS listeners (RFC 8314). Off by default. |
specta |
Derives specta::Type on every public DTO for TypeScript binding generation. No runtime effect. |
Enable both:
postcrate-core = { version = "0.1", features = ["tls", "specta"] }This crate ships in a Cargo workspace alongside two unpublished binaries:
| Crate | What it is |
|---|---|
postcrate-core |
This library. Public surface is the Service type. |
postcrate-server |
Headless daemon binary (postcrate run …). Build from source. |
postcrate-ci |
Fast-start CI variant; prints an env line on ready. Build from source. |
To run the daemon from source:
git clone git@github.com:postcrate/core.git
cd core
cargo run -p postcrate-server -- run --smtp 1025 --http 1080The TLS path is gated on the tls feature:
cargo run -p postcrate-server --features postcrate-core/tls -- run --smtp 1025 --http 10801.78 (pinned via rust-toolchain.toml).
MIT — see LICENSE.