fumi
Guides

TLS

Configure STARTTLS and implicit TLS (SMTPS) in fumi.

fumi supports both TLS modes via FumiOptions, which pass directly to smtp-server.

Modes

STARTTLS — port 587

The server advertises STARTTLS in the EHLO response. The client upgrades before sending mail. After upgrade, ctx.session.secure === true.

server.ts
import { Fumi } from "@puiusabin/fumi";
import { requireTls } from "@puiusabin/fumi/plugins/require-tls";

const app = new Fumi({
  key: Bun.file("server.key").arrayBuffer(),
  cert: Bun.file("server.cert").arrayBuffer(),
  authOptional: true,
});

// Reject MAIL FROM if client skipped STARTTLS
app.use(requireTls());

await app.listen(587);

Implicit TLS / SMTPS — port 465

The connection is TLS from the start. No STARTTLS command.

server.ts
const app = new Fumi({
  secure: true,
  key: Bun.file("server.key").arrayBuffer(),
  cert: Bun.file("server.cert").arrayBuffer(),
  authOptional: true,
});

await app.listen(465);

Enforcement

Protocol-level

Set requireTLS: true in options to let smtp-server reject MAIL FROM at the protocol level before middleware runs. This is simpler but less customizable than the requireTls() plugin.

server.ts
const app = new Fumi({
  key: Bun.file("server.key").arrayBuffer(),
  cert: Bun.file("server.cert").arrayBuffer(),
  requireTLS: true,
});

Middleware-level

For middleware-level enforcement with custom error messages, use the requireTls() plugin shown in the STARTTLS example above. It runs in onMailFrom and gives you full control over the rejection code and message.

Local development

Generating a certificate

Generate a self-signed certificate with mkcert:

brew install mkcert
mkcert -install
mkcert localhost
# produces: localhost.pem + localhost-key.pem

Passing to FumiOptions

server.ts
const app = new Fumi({
  key: await Bun.file("localhost-key.pem").text(),
  cert: await Bun.file("localhost.pem").text(),
});

mkcert certificates are trusted by your local machine only. Do not use them in production — get a certificate from a public CA (e.g. Let's Encrypt).

On this page