Guides
Plugins
Write and use plugins to share reusable SMTP logic.
A plugin is a function that receives the Fumi instance and registers its own handlers.
import type { Plugin } from "@puiusabin/fumi";
export function myPlugin(): Plugin {
return (app) => {
app.onConnect(async (ctx, next) => {
console.log(`[connect] ${ctx.session.remoteAddress}`);
await next();
});
};
}Register it with app.use():
import { Fumi } from "@puiusabin/fumi";
import { myPlugin } from "./my-plugin.ts";
const app = new Fumi({ authOptional: true });
app.use(myPlugin());
await app.listen(2525);Bundled plugins
fumi ships a small set of reference plugins. Import them from @puiusabin/fumi/plugins/*.
logger
Logs each SMTP phase to stdout.
import { logger } from "@puiusabin/fumi/plugins/logger";
app.use(logger());denylist
Blocks connections from specific IP addresses.
import { denylist } from "@puiusabin/fumi/plugins/denylist";
app.use(denylist(["1.2.3.4"]));senderBlock
Rejects mail from specific sender domains.
import { senderBlock } from "@puiusabin/fumi/plugins/sender-block";
app.use(senderBlock(["spam.example"]));rcptFilter
Only accepts recipients whose domain is in an allowed list.
import { rcptFilter } from "@puiusabin/fumi/plugins/rcpt-filter";
app.use(rcptFilter(["mycompany.com"]));maxSize
Rejects messages that exceed a byte limit. Pair with FumiOptions.size.
import { maxSize } from "@puiusabin/fumi/plugins/max-size";
const app = new Fumi({ size: 10_000_000 });
app.use(maxSize(10_000_000));requireTls
Rejects MAIL FROM on unencrypted connections (STARTTLS not completed).
import { requireTls } from "@puiusabin/fumi/plugins/require-tls";
app.use(requireTls());Writing a plugin with options
Plugins are factory functions — return a Plugin from a function that takes options.
import type { Plugin } from "@puiusabin/fumi";
export function rateLimit(opts: { max: number; windowMs: number }): Plugin {
const counts = new Map<string, number>();
return (app) => {
app.onConnect(async (ctx, next) => {
const ip = ctx.session.remoteAddress;
const count = (counts.get(ip) ?? 0) + 1;
counts.set(ip, count);
if (count > opts.max) {
ctx.reject("Too many connections", 421);
}
await next();
});
};
}