Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/puiusabin/bun-smtp/llms.txt

Use this file to discover all available pages before exploring further.

Pass options to the SMTPServer constructor:
import { SMTPServer } from "bun-smtp";

const server = new SMTPServer({
  // options here
});

Connection

secure
boolean
default:false
Start in implicit TLS mode (port 465 style). When false, STARTTLS is offered instead.
needsUpgrade
boolean
default:false
Reject AUTH and MAIL until the client completes STARTTLS.
name
string
default:"system hostname"
Server hostname included in the 220 greeting and EHLO response.
banner
string
default:""
Extra text appended to the 220 greeting line.
lmtp
boolean
default:false
Use LMTP instead of SMTP. Clients open with LHLO and onData may return per-recipient responses.
heloResponse
string
default:"%s Nice to meet you, %s"
Format string for the HELO/EHLO response. First %s is the server name, second is the client hostname.

Authentication

authMethods
string[]
default:["PLAIN","LOGIN"]
SASL methods advertised in EHLO. Supported values: "PLAIN", "LOGIN", "CRAM-MD5", "XOAUTH2".
authOptional
boolean
default:false
Allow clients to skip AUTH entirely.
allowInsecureAuth
boolean
default:false
Allow AUTH over a plain (non-TLS) connection.
authRequiredMessage
string
Custom error message for the 530 response when auth is required.
By default, AUTH is disabled over non-TLS connections. Set allowInsecureAuth: true to permit plaintext authentication.

Capability Flags

These options hide extensions from the EHLO response. The extension still works — it is just not advertised.
hideSTARTTLS
boolean
default:false
Hide STARTTLS from EHLO.
hideSize
boolean
default:false
Hide the SIZE extension.
hidePIPELINING
boolean
default:false
Hide PIPELINING.
hideDSN
boolean
default:true
Hide DSN (Delivery Status Notification).
hideENHANCEDSTATUSCODES
boolean
default:true
Hide ENHANCEDSTATUSCODES.
hideREQUIRETLS
boolean
default:true
Hide REQUIRETLS.
hide8BITMIME
boolean
default:false
Hide 8BITMIME.
hideSMTPUTF8
boolean
default:false
Hide SMTPUTF8.
disabledCommands
string[]
default:[]
Block specific SMTP commands entirely (e.g. ["AUTH", "STARTTLS"]).

Limits

size
number
Maximum message size in bytes. Advertised via the SIZE extension. The onData stream’s sizeExceeded flag is set when the limit is hit.
maxClients
number
Maximum number of simultaneous connections. New connections are rejected with 421 when the limit is reached.
socketTimeout
number
default:60000
Milliseconds of inactivity before an idle connection is closed.
closeTimeout
number
default:30000
Milliseconds to wait for connections to drain during server.close(). Connections still open after this are forcibly terminated.
maxAllowedUnauthenticatedCommands
number | false
default:10
Maximum commands allowed before authentication. Set to false to disable the limit.
Setting maxAllowedUnauthenticatedCommands to false may expose your server to abuse. Use with caution.

Proxy / X-headers

useXClient
boolean
default:false
Trust Postfix XCLIENT headers. When enabled, session.xClient is populated.
useXForward
boolean
default:false
Trust Postfix XFORWARD headers. When enabled, session.xForward is populated.
useProxy
boolean | string[]
default:false
Parse HAProxy PROXY protocol header. Pass an array of trusted proxy IP addresses to restrict which proxies are trusted.
Only enable proxy headers if you trust the upstream proxy. Malicious clients can forge these headers.

DNS

disableReverseLookup
boolean
default:false
Skip reverse DNS lookup on new connections. When false, session.clientHostname is resolved from the client’s IP.
resolver
object
Custom DNS resolver. Must implement reverse(ip, callback) with the same signature as dns.reverse.
{
  reverse: (
    ip: string,
    callback: (err: Error | null, hostnames?: string[]) => void
  ) => void
}

TLS

All standard TLS options. See the TLS & STARTTLS guide for usage examples.
key
string | Buffer
Private key in PEM format.
cert
string | Buffer
Certificate in PEM format.
ca
string | Buffer | Array
CA bundle for client certificate verification.
requestCert
boolean
Request a client certificate during TLS handshake.
rejectUnauthorized
boolean
Reject clients with invalid or unverifiable certificates.
minVersion
string
Minimum TLS version string (e.g. "TLSv1.2").
maxVersion
string
Maximum TLS version string.
sniOptions
Record<string, TLSOptions> | Map<string, TLSOptions>
Per-hostname TLS configuration for SNI (Server Name Indication).
sniOptions: {
  "mail.example.com": {
    key: exampleKey,
    cert: exampleCert
  },
  "mail.other.com": {
    key: otherKey,
    cert: otherCert
  }
}
If no TLS options are provided, the server uses a default self-signed certificate for development.

Callbacks

All lifecycle callbacks can be set as constructor options. See Callbacks for full signatures and examples.
onConnect
OnConnectCallback
Called on new connection.
(session: SMTPSession, callback: (err?: Error | null) => void) => void
onSecure
OnSecureCallback
Called after TLS handshake.
(
  socket: Socket,
  session: SMTPSession,
  callback: (err?: Error | null) => void
) => void
onAuth
OnAuthCallback
Called on AUTH attempt.
(
  auth: AuthObject,
  session: SMTPSession,
  callback: (err: Error | null, response?: AuthResponse) => void
) => void
onMailFrom
OnMailFromCallback
Called on MAIL FROM.
(
  address: SMTPAddress,
  session: SMTPSession,
  callback: (err?: Error | null) => void
) => void
onRcptTo
OnRcptToCallback
Called on RCPT TO.
(
  address: SMTPAddress,
  session: SMTPSession,
  callback: (err?: Error | null) => void
) => void
onData
OnDataCallback
Called when DATA transfer begins.
(
  stream: DataStream,
  session: SMTPSession,
  callback: (
    err: Error | null,
    message?: string | Array<string | SMTPError>
  ) => void
) => void
onClose
OnCloseCallback
Called when connection closes.
(session: SMTPSession) => void

Complete Example

import { SMTPServer } from "bun-smtp";

const server = new SMTPServer({
  // Connection
  secure: false,
  name: "mail.example.com",
  banner: "Welcome to Example Mail",
  
  // Authentication
  authMethods: ["PLAIN", "LOGIN", "CRAM-MD5"],
  authOptional: false,
  allowInsecureAuth: false,
  
  // Limits
  size: 10 * 1024 * 1024, // 10 MB
  maxClients: 100,
  socketTimeout: 60000,
  
  // TLS
  key: await Bun.file("key.pem").text(),
  cert: await Bun.file("cert.pem").text(),
  
  // Callbacks
  onAuth(auth, session, callback) {
    if (auth.username === "user" && auth.password === "pass") {
      callback(null, { user: auth.username });
    } else {
      callback(new Error("Invalid credentials"));
    }
  },
  
  onData(stream, session, callback) {
    const chunks = [];
    stream.pipeTo(new WritableStream({
      write(chunk) {
        chunks.push(chunk);
      },
      close() {
        const message = Buffer.concat(chunks);
        console.log(`Received ${message.length} bytes`);
        callback(null);
      },
      abort(err) {
        callback(err);
      }
    }));
  }
});

server.listen(2525);