Skip to content

6mmLIU/legacy-mail-service

Repository files navigation

Legacy Mail Service

Self-hosted receive-only inboxes for OTP testing, QA automation, and private disposable mail workflows.

English · 中文

Node.js Docker License Mail mode

Mailbridge inbox dashboard

Screenshots

Inbox dashboard Mailbox categories
Access control settings

What Is This?

Legacy Mail Service is a small, production-oriented Node.js service for receiving email on domains you control. It stores inbound messages locally, provides a polished Mailbridge admin UI, and exposes API endpoints that make it easy to create test inboxes and read verification codes from E2E automation.

It is intentionally receive-only. It does not send email, relay outbound SMTP, host IMAP mailboxes, or try to replace Mailcow. It is built for private disposable inboxes, signup testing, OTP capture, QA runs, and developer tooling.

Highlights

  • Receive inbound email through a built-in SMTP server.
  • Create disposable addresses under your own domains.
  • Use the admin UI at /ui to inspect mail, unread state, categories, and extracted OTP codes.
  • Read messages from Playwright, Puppeteer, Cypress, shell scripts, or backend jobs through a small token-authenticated API.
  • Keep the codex-registrar-lite legacy provider contract through POST /admin/new_address.
  • Group inboxes manually or through rule-based categories.
  • Render HTML email safely in sandboxed iframes while blocking scripts.
  • Restrict the admin/API surface by fixed client IP allowlists.
  • Deploy with Docker Compose or systemd behind Caddy/Nginx.
  • Validate deployments with included health, DNS, and smoke-test scripts.

When To Use It

Use Legacy Mail Service when you need:

  • Private temporary inboxes instead of public disposable-mail services.
  • A stable domain for account registration and login-code testing.
  • Automated verification-code polling in browser tests.
  • A lightweight inbound-only mail sink for staging and QA.
  • A self-hosted service that is easy to audit and operate.

Do not use it as a general mailbox platform. If you need real users, IMAP, outbound SMTP, spam filtering, aliases, calendars, or full mail administration, use a complete mail suite instead.

Architecture

Internet email
    |
    v
DNS MX record -> VPS port 25 -> Legacy Mail SMTP receiver
                                      |
                                      v
                              local JSON store
                                      |
                  +-------------------+-------------------+
                  |                                       |
                  v                                       v
          Mailbridge admin UI                        JSON APIs
          /ui                                      /admin, /api

The service keeps the operational model deliberately small:

  • One Node.js process runs the HTTP API/admin UI and SMTP receiver.
  • Mail data is persisted in a local JSON store under DATA_DIR.
  • HTTPS is handled by your reverse proxy, usually Caddy or Nginx.
  • SMTP is exposed on port 25; the API/UI can stay on localhost behind the proxy.

Quick Start With Docker

git clone https://github.com/6mmLIU/legacy-mail-service.git
cd legacy-mail-service
cp .env.example .env

Edit .env before starting:

ADMIN_USERNAME=admin
ADMIN_PASSWORD=replace-with-a-long-random-password
TOKEN_SECRET=replace-with-another-long-random-secret
ALLOWED_DOMAINS=example.com

Start the service:

docker compose up -d --build

Open the admin UI:

http://127.0.0.1:3000/ui

Check health:

LEGACY_MAIL_API_BASE_URL=http://127.0.0.1:3000 npm run healthcheck

The Compose file maps host SMTP port 25 to container port 2525, binds the HTTP UI/API to 127.0.0.1:3000, and stores mail in the legacy-mail-data Docker volume.

Local Development

For development, use a high SMTP port so you do not need root privileges:

npm install
ADMIN_USERNAME=admin \
ADMIN_PASSWORD=dev-password \
TOKEN_SECRET=dev-secret \
ALLOWED_DOMAINS=example.test \
SMTP_PORT=2525 \
npm start

Then open:

http://127.0.0.1:3000/ui

Production Deployment

1. Prepare DNS

Point the mail host and MX record to your server:

A    mail.example.com    <server-ip>
MX   example.com         mail.example.com    priority 10

Validate from your workstation or server:

LEGACY_MAIL_DOMAIN=example.com \
LEGACY_MAIL_MX_HOST=mail.example.com \
bash scripts/check-domain-dns.sh

2. Open The Required Ports

Expose these public ports:

25/tcp   inbound SMTP
80/tcp   HTTP challenge or redirect
443/tcp  HTTPS admin/API

Keep 3000/tcp private unless you intentionally want to expose the raw Node.js server.

3. Run With systemd

Copy the repository to /opt/legacy-mail, create /etc/legacy-mail.env from .env.example, then install dependencies:

cd /opt/legacy-mail
npm install --omit=dev --ignore-scripts
sudo cp deploy/legacy-mail.service /etc/systemd/system/legacy-mail.service
sudo systemctl daemon-reload
sudo systemctl enable --now legacy-mail

Verify locally:

curl -fsS http://127.0.0.1:3000/health

4. Add HTTPS Reverse Proxy

Caddy example:

mail-api.example.com {
  reverse_proxy 127.0.0.1:3000
}

Set TRUST_PROXY=true when the reverse proxy should provide the real client IP through X-Forwarded-For.

5. Smoke Test

After the service is running:

LEGACY_MAIL_API_BASE_URL=https://mail-api.example.com \
LEGACY_MAIL_ADMIN_PASSWORD=<ADMIN_PASSWORD> \
LEGACY_MAIL_TEST_DOMAIN=example.com \
LEGACY_MAIL_SMTP_HOST=mail.example.com \
LEGACY_MAIL_SMTP_PORT=25 \
npm run smoke

API Examples

Create an address:

curl -sS https://mail-api.example.com/admin/new_address \
  -H "content-type: application/json" \
  -H "x-admin-auth: $ADMIN_PASSWORD" \
  -d '{"name":"openai-login","domain":"example.com"}'

Read messages with the returned token:

curl -sS "https://mail-api.example.com/api/mails?limit=10" \
  -H "authorization: Bearer $ADDRESS_TOKEN"

Typical mail records include subject, from, to, text, html, unread, and verificationCode.

Core endpoints:

Endpoint Purpose
GET /health Runtime health and non-secret counts
POST /admin/new_address Create an address and token
GET /api/mails List mails for a token or admin filter
POST /api/mails/:id/read Mark a message as read
DELETE /api/mails/:id Delete a message
GET /openapi.json OpenAPI metadata

See docs/api.md for the full reference.

Environment Variables

Variable Default Description
API_HOST 0.0.0.0 HTTP bind host
API_PORT 3000 HTTP bind port
SMTP_PORT 25 SMTP receiver port
DATA_DIR ./data Persistent store directory
ADMIN_USERNAME admin Admin UI username
ADMIN_PASSWORD required Admin UI/API password
SITE_PASSWORD empty Optional extra site password
TOKEN_SECRET ADMIN_PASSWORD fallback JWT/HMAC signing secret
ALLOWED_DOMAINS * Comma-separated accepted recipient domains
TRUST_PROXY false Trust X-Forwarded-For from a proxy
SESSION_TTL_MS 43200000 Admin session lifetime
MAX_HTTP_BODY_BYTES 262144 Maximum HTTP request body size
MAX_MAIL_BYTES 10485760 Maximum accepted SMTP message size

Repository Layout

server.js                         Node.js HTTP UI/API and SMTP receiver
docker-compose.yml                Docker deployment
deploy/legacy-mail.service        systemd unit
scripts/                          DNS, health, smoke, and deployment helpers
docs/api.md                       API reference
docs/deployment.md                Deployment guide
docs/dns.md                       DNS and MX guide
docs/automation.md                Automation examples
specs/000-open-source-foundation  Product spec and implementation plan

Security Notes

  • Replace ADMIN_PASSWORD and TOKEN_SECRET before production.
  • Keep the raw API/UI port private and put HTTPS in front of it.
  • Use the fixed IP allowlist when the admin panel should only be reachable from known client IPs.
  • The service receives mail only. It does not relay outbound SMTP.
  • HTML mail is rendered in a sandboxed iframe and scripts are blocked.
  • Treat the JSON store as sensitive because it contains received email content and tokens.

Documentation

License

MIT. See LICENSE.

About

Self-hosted temporary mail receiver with SMTP, HTTP API, and admin UI

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors