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.

Every callback receives a session object representing the current connection state.

SMTPSession

The main session object passed to all callbacks.
id
string
Unique connection identifier.
secure
boolean
true if the connection is currently using TLS.
servername
string | undefined
SNI hostname from the TLS handshake (if TLS is active).
localAddress
string
Server IP address.
localPort
number
Server port.
remoteAddress
string
Client IP address.
remotePort
number
Client port.
clientHostname
string
Reverse-DNS hostname of the client. Falls back to "[remoteAddress]" if lookup fails or is disabled.
hostNameAppearsAs
string
Hostname the client claimed in HELO/EHLO.
openingCommand
string
The first command the client sent: "HELO", "EHLO", or "LHLO".
transmissionType
string
SMTP transmission type string, e.g. "ESMTPSA" (Extended SMTP with Auth).
tlsOptions
TLSCipherInfo | false
TLS cipher info after TLS is established, false before. See TLSCipherInfo below.
user
unknown
Value passed as user in a successful onAuth response. Use this to track the authenticated user.
transaction
number
Number of completed mail transactions on this connection (incremented after each successful onData).
envelope
SMTPEnvelope
Current mail envelope. See SMTPEnvelope below.
xClient
Map<string, string | false>
XCLIENT header values (when useXClient: true).
xForward
Map<string, string | false>
XFORWARD header values (when useXForward: true).

TLSCipherInfo

TLS cipher and protocol information, available after TLS handshake.
name
string
OpenSSL cipher name (e.g. "ECDHE-RSA-AES128-GCM-SHA256").
standardName
string | undefined
IANA cipher name.
version
string | undefined
TLS protocol version (e.g. "TLSv1.3").

Example

const server = new SMTPServer({
  onSecure(socket, session, callback) {
    console.log(session.tlsOptions);
    // {
    //   name: "ECDHE-RSA-AES128-GCM-SHA256",
    //   standardName: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
    //   version: "TLSv1.2"
    // }
    callback(null);
  },
});

SMTPEnvelope

Available as session.envelope. Populated progressively as the client sends MAIL FROM and RCPT TO commands.
mailFrom
SMTPAddress | false
Sender address from MAIL FROM, or false before MAIL FROM is received.
rcptTo
SMTPAddress[]
Array of accepted recipient addresses from RCPT TO.
bodyType
'7bit' | '8bitmime'
Body encoding declared by the client.
smtpUtf8
boolean
true if the client declared SMTPUTF8 support.
requireTLS
boolean
true if the client sent REQUIRETLS (RFC 8689).
dsn
DSNEnvelope | undefined
DSN envelope parameters. See DSNEnvelope below.

Example

const server = new SMTPServer({
  onData(stream, session, callback) {
    const { envelope } = session;
    
    console.log("From:", envelope.mailFrom?.address);
    console.log("To:", envelope.rcptTo.map(r => r.address));
    console.log("UTF-8:", envelope.smtpUtf8);
    
    stream.pipeTo(new WritableStream()).then(
      () => callback(null),
      callback
    );
  },
});

SMTPAddress

Represents an email address from MAIL FROM or RCPT TO commands.
address
string
The email address (e.g. "alice@example.com").
args
SMTPAddressArgs | false
ESMTP parameters from the command, or false if none. See SMTPAddressArgs below.
dsn
DSNRcpt | undefined
Per-recipient DSN parameters. See DSNRcpt below.

SMTPAddressArgs

ESMTP parameters parsed from the MAIL FROM or RCPT TO command.
SIZE
string | undefined
Declared message size in bytes.
BODY
string | undefined
Body type: "7BIT" or "8BITMIME".
SMTPUTF8
true | undefined
UTF-8 support flag.
REQUIRETLS
true | undefined
TLS-required flag (RFC 8689).
RET
string | undefined
DSN return type ("FULL" or "HDRS").
ENVID
string | undefined
DSN envelope ID.
NOTIFY
string | undefined
DSN notification conditions (comma-separated, e.g. "SUCCESS,FAILURE").
ORCPT
string | undefined
DSN original recipient address.
[key: string]
string | true | undefined
Any unrecognized ESMTP parameter is also available as a string or true.

Example

const server = new SMTPServer({
  onMailFrom(address, session, callback) {
    console.log("Sender:", address.address);
    
    if (address.args) {
      console.log("Declared size:", address.args.SIZE);
      console.log("Body type:", address.args.BODY);
      console.log("UTF-8:", address.args.SMTPUTF8);
    }
    
    callback(null);
  },
});

DSNEnvelope

DSN (Delivery Status Notification) parameters from the envelope.
ret
'FULL' | 'HDRS' | null
Return type requested by the client:
  • "FULL": Return full message in DSN
  • "HDRS": Return headers only
  • null: No preference specified
envid
string | null
Envelope identifier for tracking DSN reports.

DSNRcpt

Per-recipient DSN parameters.
notify
string[] | undefined
DSN notification conditions (e.g. ["SUCCESS", "FAILURE", "DELAY"]).
orcpt
string | undefined
Original recipient address (for forwarding scenarios).

Example

const server = new SMTPServer({
  onRcptTo(address, session, callback) {
    console.log("Recipient:", address.address);
    
    if (address.dsn) {
      console.log("Notify on:", address.dsn.notify);
      console.log("Original:", address.dsn.orcpt);
    }
    
    callback(null);
  },
});

DataStream

A ReadableStream<Uint8Array> with extra metadata set after the stream closes.
byteLength
number | undefined
Total bytes received (available after stream closes).
sizeExceeded
boolean | undefined
true if the message exceeded the configured size limit.

Example

const server = new SMTPServer({
  size: 10 * 1024 * 1024, // 10 MB
  onData(stream, session, callback) {
    async function process() {
      for await (const chunk of stream) {
        // process chunks
      }
      
      console.log("Received:", stream.byteLength, "bytes");
      
      if (stream.sizeExceeded) {
        const err = new Error("Message too large");
        err.responseCode = 552;
        return callback(err);
      }
      
      callback(null);
    }
    process().catch(callback);
  },
});

Complete Example

import { SMTPServer } from "bun-smtp";

const server = new SMTPServer({
  onConnect(session, callback) {
    console.log(`New connection from ${session.remoteAddress}`);
    callback(null);
  },
  
  onAuth(auth, session, callback) {
    callback(null, { user: { username: auth.username } });
  },
  
  onData(stream, session, callback) {
    const { envelope, user } = session;
    
    console.log("Connection ID:", session.id);
    console.log("Secure:", session.secure);
    console.log("TLS:", session.tlsOptions);
    console.log("User:", user);
    console.log("From:", envelope.mailFrom?.address);
    console.log("To:", envelope.rcptTo.map(r => r.address));
    console.log("UTF-8:", envelope.smtpUtf8);
    console.log("Transaction #", session.transaction);
    
    stream.pipeTo(new WritableStream()).then(
      () => {
        console.log("Received:", stream.byteLength, "bytes");
        callback(null);
      },
      callback
    );
  },
  
  onClose(session) {
    console.log(`Connection ${session.id} closed`);
  },
});

server.listen(2525);

Build docs developers (and LLMs) love