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.

This example shows how to create a basic SMTP server that accepts emails without authentication and saves them to disk.

Complete example

server.ts
import { SMTPServer } from "bun-smtp";
import type { DataStream, SMTPSession } from "bun-smtp";

const server = new SMTPServer({
  authOptional: true,
  onData(stream: DataStream, session: SMTPSession, callback) {
    async function saveEmail() {
      const chunks: Uint8Array[] = [];
      
      // Collect all chunks from the stream
      for await (const chunk of stream) {
        chunks.push(chunk);
      }
      
      // Combine chunks into a single buffer
      const emailContent = Buffer.concat(chunks);
      
      // Save to disk with timestamp
      const filename = `email-${Date.now()}.eml`;
      await Bun.write(filename, emailContent);
      
      console.log(`Saved email to ${filename}`);
      console.log(`From: ${session.envelope.mailFrom?.address}`);
      console.log(`To: ${session.envelope.rcptTo.map(r => r.address).join(", ")}`);
      
      callback(null);
    }
    
    saveEmail().catch(callback);
  },
});

await server.listen(2525);
console.log("SMTP server listening on port 2525");

How it works

1

Configure the server

Set authOptional: true to allow clients to send mail without authenticating.
2

Handle incoming data

The onData callback receives a ReadableStream<Uint8Array> containing the email message.
3

Process the stream

Use a for await...of loop to consume all chunks from the stream.
4

Save the email

Use Bun.write() to save the combined buffer to disk as a .eml file.
5

Complete the transaction

Call callback(null) to send a success response to the client.
You must fully consume the stream before calling the callback. Otherwise, the client will hang waiting for a response.

Testing the server

Send a test email using the mail command or telnet:
telnet localhost 2525
Then type:
HELO localhost
MAIL FROM:<sender@example.com>
RCPT TO:<recipient@example.com>
DATA
Subject: Test Email

This is a test message.
.
QUIT

Accessing envelope information

The session.envelope object contains metadata about the email:
onData(stream, session, callback) {
  console.log("Sender:", session.envelope.mailFrom?.address);
  console.log("Recipients:", session.envelope.rcptTo.map(r => r.address));
  console.log("Body type:", session.envelope.bodyType); // "7bit" or "8bitmime"
  console.log("UTF8:", session.envelope.smtpUtf8);
  console.log("Require TLS:", session.envelope.requireTLS);
  
  // ... process stream
}

Error handling

Reject the email with a custom error code:
onData(stream, session, callback) {
  async function process() {
    const chunks: Uint8Array[] = [];
    for await (const chunk of stream) {
      chunks.push(chunk);
    }
    
    const content = Buffer.concat(chunks).toString();
    
    // Reject spam
    if (content.includes("SPAM")) {
      const err = new Error("Spam detected") as any;
      err.responseCode = 550;
      return callback(err);
    }
    
    callback(null);
  }
  
  process().catch(callback);
}
Always consume the entire stream even if you plan to reject the message. Otherwise, the connection may hang.

Size limits

Set a maximum message size:
const server = new SMTPServer({
  authOptional: true,
  size: 10 * 1024 * 1024, // 10 MB limit
  onData(stream, session, callback) {
    async function process() {
      for await (const chunk of stream) {
        // drain
      }
      
      if (stream.sizeExceeded) {
        const err = new Error("Message too large") as any;
        err.responseCode = 552;
        return callback(err);
      }
      
      console.log(`Received ${stream.byteLength} bytes`);
      callback(null);
    }
    
    process().catch(callback);
  },
});

Next steps

Add authentication

Require users to authenticate before sending

Enable TLS

Encrypt connections with STARTTLS

Configuration reference

Explore all server options

Callbacks reference

Learn about all lifecycle callbacks