Skip to main content

Welcome to Fumi

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(2525);

Key Features

Ultrafast

Runs natively on Bun. No adapter layer, no Node.js compatibility overhead.

Lightweight

Tiny core with one runtime dependency (bun-smtp). Minimal footprint.

Middleware System

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.

TypeScript First

First-class TypeScript support with complete type definitions for all contexts.

Delightful DX

Super clean APIs with intuitive phase-based middleware registration.

SMTP Phase Middleware

Fumi provides middleware hooks for every SMTP phase:
  • onConnect - Validate connections before accepting them
  • onAuth - Implement custom authentication logic
  • onMailFrom - Validate sender addresses
  • onRcptTo - Validate recipient addresses
  • onData - Process email message data
  • onClose - Cleanup when connections close
Each middleware receives a context object with session information and a next() function to continue the chain.

Built-in Plugins

Fumi includes several ready-to-use plugins:
  • logger - Logs each SMTP phase to stdout
  • denylist - Blocks connections from specific IP addresses
  • senderBlock - Rejects mail from blocked domains
  • rcptFilter - Restricts recipients to allowed domains
  • requireTls - Enforces TLS encryption
  • maxSize - Rejects oversized messages

Get Started

Installation

Install Fumi with Bun and verify your setup

Quick Start

Build your first SMTP server in minutes

Middleware Guide

Learn how to use middleware to control SMTP phases

Plugin Development

Create and share your own Fumi plugins
STARTTLS is not production-ready. Bun does not support socket.upgradeTLS() on server-side sockets, which means the STARTTLS command will crash or silently fail. In production, use implicit TLS (port 465) or terminate TLS externally with HAProxy or stunnel.

Build docs developers (and LLMs) love