Introduction
Modern, ultrafast lightweight SMTP Server.
fumi - means letter✉️ in Japanese - is a small, simple, and ultrafast SMTP Server framework built on Bun.
import { Fumi } from "@puiusabin/fumi";
const app = new Fumi({ authOptional: true });
app.onMailFrom(async (ctx, next) => {
if (ctx.address.address.endsWith("@blocked.example")) {
ctx.reject("Domain blocked", 550);
}
await next();
});
await app.listen(25);Quick Start
bun add @puiusabin/fumiFeatures
- Ultrafast 🚀 - Runs natively on Bun. No adapter layer, no Node.js compat overhead.
- Lightweight 🪶 - Tiny core. One runtime dependency (
bun-smtp). - Middleware 🔗 - koa-style
(ctx, next)chains per SMTP phase: connect, auth, mailFrom, rcptTo, data, close. - Plugin system 🔌 - A plugin is just
(app: Fumi) => void. No registry, no lifecycle hooks. - Delightful DX 😃 - Super clean APIs. First-class TypeScript support.
Use Cases
fumi is a minimal SMTP server framework for Bun. It handles the full SMTP lifecycle through composable middleware and runs anywhere Bun runs. Here are some examples of use cases.
- Inbound email processing
- Development mail catcher
- Email gateway
- Spam filter layer
- Transactional email relay
- Custom mail server
Why fumi?
Compared to Haraka
Haraka is the closest prior art — a plugin-based SMTP server for Node.js. fumi differs in a few ways:
- Built for Bun, not Node.js
- Middleware instead of hooks/callbacks
- TypeScript types throughout, no
@types/*packages needed ctx.reject()returnsnever— the compiler enforces control flow
Design
SMTP phases
Each SMTP phase gets its own independent middleware stack:
| Phase | Method | Context |
|---|---|---|
| Connection | onConnect | ConnectContext |
| Authentication | onAuth | AuthContext |
| MAIL FROM | onMailFrom | MailFromContext |
| RCPT TO | onRcptTo | RcptToContext |
| DATA | onData | DataContext |
| Close | onClose | CloseContext |
Independent stacks
Phases are independent — a single global middleware chain would require unsafe union types across all contexts.