| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| README.md | 2026-09-23 | 9.5 kB | |
| v3.0.0 source code.tar.gz | 2026-09-23 | 1.3 MB | |
| v3.0.0 source code.zip | 2026-09-23 | 1.4 MB | |
| Totals: 3 Items | 2.8 MB | 0 | |
Major Changes
- #525
8aafc97Thanks @djfarrelly! - Complete project re-build
Patch Changes
- #550
d34aaa3Thanks @djfarrelly! - Fix the Docker healthcheck so containers report healthy out of the box. A dedicated healthcheck entrypoint (dist/bin/healthcheck.js) now: - probes
127.0.0.1instead oflocalhost, so it no longer fails whenlocalhostresolves to IPv6 (::1) while the web server binds IPv4 only (#537); - falls back to a TCP check on the SMTP port when the web UI is disabled with
--disable-web, instead of probing an endpoint that isn't there (#544); -
normalizes
MAILDEV_BASE_PATHNAMEso a trailing slash can't produce a//in the probe URL (#542). -
#578
a2eb3a1Thanks @djfarrelly! - Honorport: 0and expose the bound port on the programmatic API (#567).
Previously new MailDev({ smtp: 0 }) was silently ignored — options.port || DEFAULT_PORT treated an explicit 0 as absent and fell back to 1025. It now uses ??, so 0 requests an OS-assigned ephemeral port, and the port that was actually bound is read back from the listening socket.
Both servers now expose their bound address:
SMTPServer#getAddress()→{ host, port }andSMTPServer#getPort()APIServer#getAddress()→{ host, port } | nullandAPIServer#getPort()(null until listening)
Together these let you run one MailDev per worker under a parallel test runner without hand-assigning ports.
-
#581
0e79a6aThanks @cpruijsen! - HonorMAILDEV_AUTO_RELAYandMAILDEV_AUTO_RELAY_RULESso Docker and other env-based setups can enable auto-relay the same way--auto-relayand--auto-relay-rulesdo.true,1, and a present-but-empty value relay to each mail's own recipients;falseand0disable; any other value is the override recipient. -
4b515c6Thanks @djfarrelly! - Fix the integrated MCP HTTP transport (maildev --mcp) so it supports more than one session. Each session now gets its own MCP server instance instead of sharing a single one, which previously made the second client fail to connect with "Already connected to a transport." Requests carrying an unknown session ID now return a proper JSON-RPC error, and open MCP sessions are closed on shutdown. -
#555
196f277Thanks @djfarrelly! - Serve the web UI / REST API over HTTPS when--https(with--https-certand--https-key) is set. The Fastify server now actually honors these options — previously the flags existed but the web server always served plain HTTP. HTTPS can also be configured viaMAILDEV_HTTPS,MAILDEV_HTTPS_CERT, andMAILDEV_HTTPS_KEY, and the Docker healthcheck detectsMAILDEV_HTTPSand probes over HTTPS so TLS-enabled containers report healthy. -
42d3708Thanks @djfarrelly! - Add an opt-in maxEmails limit that also bounds the mail directory
An opt-in maxEmails limit (default 0 = unlimited; set --max-emails to cap)
discards the oldest emails along with their .eml files and attachments, so both
memory and the mail directory can be kept bounded. When set, leftover files from
earlier runs are trimmed at startup. The default keeps MailDev's historical
unbounded behaviour, so persisted mail stays durable across restarts (no data
loss by default).
Breaking change:
-
@maildev/core:Storageimplementations must now provideonEvicted. A newEvictHandlertype andmapLimithelper are exported. -
#551
fc7e584Thanks @djfarrelly! - Add a configurable maximum message size and reject oversized messages. A new--max-message-sizeoption (envMAILDEV_MAX_MESSAGE_SIZE, default 50 MB) advertises the SMTP SIZE extension and refuses messages larger than the limit. The bytes forwarded to the parser are capped at the limit, so a malicious multipart message with a huge number of parts can no longer tie up the parser (addresses the unbounded MIME sibling-part fanout in #531). Set to 0 to disable the limit. -
#579
f78e761Thanks @djfarrelly! - Record relay delivery status on the email. After a successful relay, the stored email now carriesrelayedAt(when it was last relayed) andrelayedTo(the recipients it was delivered to), so the REST API can report whether a message was relayed and to which addresses. This hooks into the sharedrelayEmailpath used by both auto-relay and the manual relay endpoint (closes #199). -
#549
781cc0cThanks @djfarrelly! - Restore persisted emails on startup. When--mail-directory(MAILDEV_MAIL_DIRECTORY) is set, existing.emlfiles in the directory are now loaded back into the UI when MailDev starts, so mail survives a restart (e.g. across container/pod restarts with a mounted volume). -
7af89efThanks @djfarrelly! - Rejectstart()on an SMTP port conflict instead of crashing the process (#568).
When the SMTP port was unavailable (e.g. EADDRINUSE), the error arrived via the server's error event and was re-thrown from inside that callback. It escaped as an uncaught exception and took the process down, while start()'s promise stayed pending forever — a try/catch around await start() never saw it.
Startup errors are now routed to the start() promise, so await maildev.start() rejects with the underlying error (e.g. EADDRINUSE) and the caller decides what to do. Runtime errors after startup remain observable via the error event, but a listener-less emitter is no longer fatal.