npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

marla-mail

v1.2.0

Published

She catches your mail so it never leaves. A local SMTP trap with a live web inbox for testing email in development.

Readme

Marla

She catches your mail so it never leaves.

A local SMTP trap with a live web inbox — for testing email in development. Point your app's SMTP at Marla and every message it sends lands in a clean, real-time inbox instead of a real recipient. Nothing is ever relayed onward.

A self-hosted, SMTP-only take on Mailtrap/MailHog/Mailpit: no accounts, no cloud, no sending — just catch and inspect.

Features

  • Catches all outgoing SMTP on port 1025 and never relays it. No auth required (accepted if offered); STARTTLS is not advertised.
  • Live web inbox at http://localhost:8025 — new mail appears instantly over WebSocket, with a small visual cue when one lands.
  • Read any message: rendered HTML in a sandboxed iframe, plain text, full raw source, and headers.
  • Attachments: thumbnails for images, file chips for the rest, all downloadable. Inline cid: images are rewritten to display correctly.
  • SMTP envelope: see the real MAIL FROM / RCPT TO alongside the header From/To.
  • Light & dark themes, responsive to mobile, keyboard-navigable, reduced-motion aware.
  • In-memory only — a ring buffer capped at 500 messages. Restart clears everything by design.
  • Change the SMTP port and connection security live from the UI (click the port pill) — plaintext, STARTTLS, or implicit TLS, rebinds with no restart.
  • Self-contained — fonts are bundled; works fully offline.

Quick start

npx marla-mail

Or install it as a command:

npm install -g marla-mail
marla

Or clone and run:

git clone https://github.com/robertocemeri/marla-mail.git
cd marla-mail
npm install
npm start

You'll see:

Marla SMTP trap listening on :1025
Marla inbox: http://localhost:8025

Open http://localhost:8025 and leave it open.

Keep Marla running (no terminal)

Tired of starting Marla by hand every time? Run it in the background so the inbox is always there at http://localhost:8025.

marla start      # run in the background; survives closing the terminal
marla status     # is it running? on which ports?
marla stop       # stop it
marla restart    # stop + start
marla logs       # show the background log (--follow to tail it)

To have Marla come back automatically after a reboot, register it to auto-start on login (works on macOS, Linux, and Windows):

marla install    # start now and on every login
marla uninstall  # stop and remove auto-start

start and install take the same --smtp-port / --http-port / SMTP_SECURITY options as the foreground command, and install remembers them across reboots:

marla install --smtp-port 2525 --http-port 9000

Notes:

  • For auto-start, install Marla globally (npm install -g marla-mail) rather than running it from npx or a project folder — login services need a stable path they can read. On macOS, that also means not from ~/Desktop, ~/Documents, or ~/Downloads, which the OS keeps private from background agents; marla install will warn you if you try.
  • These commands manage a single background instance. Running extra copies on other ports (e.g. HTTP_PORT=8026 marla) still works but isn't tracked by status / stop.
  • Running Marla in the foreground (plain marla, Ctrl-C to quit) works exactly as before.

Send a test message

No app needed — the bundled script sends one HTML email with an attachment and an inline image:

npm run test:send   # or: node test-send.js

Point your app at Marla

Use these SMTP settings in any app or framework:

| Setting | Value | | --- | --- | | Host | localhost (or 127.0.0.1) | | Port | 1025 | | Auth | none | | TLS / SSL / STARTTLS | off (default) — see Connection security below |

A few examples:

Nodemailer

const transport = require('nodemailer').createTransport({
  host: 'localhost', port: 1025, secure: false,
});

Laravel (.env)

MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=1025
MAIL_ENCRYPTION=null

Django (settings.py)

EMAIL_HOST = 'localhost'
EMAIL_PORT = 1025
EMAIL_USE_TLS = False

Connection security

Some apps default to wanting TLS and refuse to send to a plaintext server. Marla supports three connection modes, switchable live from the inbox Settings (click the SMTP :1025 pill) or via the SMTP_SECURITY env var. TLS modes use an auto-generated self-signed certificate, so clients connect with rejectUnauthorized: false.

| Mode | Client setup | Use when | | --- | --- | --- | | plaintext (default) | secure: false | Simplest; most dev setups | | starttls | secure: false, rejectUnauthorized: false | Your app requires/attempts a STARTTLS upgrade | | tls | secure: true, rejectUnauthorized: false | Your app uses implicit TLS (SSL on connect) |

Example for a secure: true app (Nodemailer):

require('nodemailer').createTransport({
  host: 'localhost', port: 1025, secure: true,
  tls: { rejectUnauthorized: false },   // self-signed cert
});

Options

Both ports are configurable via flags or environment variables.

marla --smtp-port 2525 --http-port 9000
# or
SMTP_PORT=2525 HTTP_PORT=9000 marla

| Flag | Env var | Default | What | | --- | --- | --- | --- | | -s, --smtp-port | SMTP_PORT | 1025 | Port to catch mail on | | -p, --http-port | HTTP_PORT | 8025 | Web inbox port | | | SMTP_SECURITY | plaintext | Connection security: plaintext, starttls, or tls | | -h, --help | | | Show help | | -v, --version | | | Print version |

The SMTP port can also be changed at runtime from the inbox UI — click the SMTP :1025 pill in the top bar. Your choice is remembered in ~/.marla.json.

API

| Method | Path | Returns | | --- | --- | --- | | GET | /api/messages | List of message summaries | | GET | /api/messages/:id | Full parsed message | | GET | /api/messages/:id/html | Rendered HTML body (cid: rewritten) | | GET | /api/messages/:id/raw | Raw source (?download=1 for .eml) | | GET | /api/messages/:id/attachments/:index | Attachment (?download=1 to force download) | | DELETE | /api/messages/:id | Delete one | | DELETE | /api/messages | Clear all | | WS | /ws | {type: "new" \| "delete" \| "clear" \| "settings", ...} |

How it works

smtp-server accepts the connection and buffers the message; mailparser parses it; it's kept in an in-memory ring buffer (raw source + parsed form). express serves the API and static UI, and ws pushes new mail to the inbox live. It never opens an outbound connection — caught mail goes nowhere else.

Notes

  • Nothing is persisted. A restart starts with an empty inbox. This is intentional — it's a scratch space for development.
  • Not for production. It accepts mail without authentication and is meant to run on your own machine.

License

MIT. Bundled Geist / Geist Mono fonts are under the SIL Open Font License 1.1.