Skip to content

Repository files navigation

mailFastApi

High-performance Node.js email microservice with split core/web architecture.

GPLv3 community release: this project is licensed under the GNU General Public License v3.0. The goal is to provide an open, inspectable mail queueing, caching, delivery, and operations platform that the community can run, study, fork, and improve.

Core design:

  • Incoming email requests are validated, checked for idempotency/suppression, and written to Redis queue immediately.
  • Worker processes consume leased Redis jobs and deliver via pooled SMTP accounts.
  • System logs are persisted to SQLite and file logs simultaneously.
  • Monitor web panel is served by a separate web service.
  • SMTP credentials and web admin password verifier are stored in an encrypted SQLite vault.

Architecture

Client -> Core API (/send, /auth/token, /health) -> Queue -> Worker -> SMTP Provider
                                     \-> Structured Logger -> SQLite + File + Console
Web Panel Service (:8080 root) -> Core Monitor APIs (/monitor/stats, /monitor/stream, /metrics)
                 -> SMTP Account Manager + Secure Update Control (scripts/updater.js)

Web Panel Preview

The legacy web panel is served directly from the root of port 8080. Screenshots below were captured from a local development run on 2026-05-27.

Secure Web Login

MailFastApi legacy web login

Legacy Monitor - Desktop

MailFastApi legacy monitor desktop

Legacy Monitor - Mobile

MailFastApi legacy monitor mobile

Encrypted Application Settings

MailFastApi encrypted settings page

Key Features

  • Fast ACK pattern (202 queued) without waiting SMTP round-trip
  • Redis-backed mail queue (QUEUE_BACKEND=redis) with processing leases, visibility timeout, and ack
  • API/worker role split with MAILFASTAPI_ROLE=api|worker|all
  • Production safety guard with PRODUCTION_MODE=true
  • Idempotency records, lifecycle states, suppression list, delivery events, dead-letter jobs, and hash-chained audit events in operational SQLite
  • Domain/account delivery policies for Gmail, Outlook, Yahoo, and corporate mail systems
  • Bounce classifier plus protected hard-bounce and complaint webhook ingestion
  • One-click unsubscribe support for marketing/bulk mail
  • Dedicated Return-Path generation and optional DKIM signing
  • SPF/DKIM/DMARC/MX/MTA-STS/TLS-RPT domain health diagnostics on the monitor API
  • Cached Nodemailer pooled transporters per SMTP account
  • Encrypted SMTP account vault (data/mailfastapi-secure.sqlite) protected by SECURE_STORE_KEY
  • First-run web panel password setup, optional TOTP MFA, session cookies, CSRF protection, and login lockout
  • Optional per-mail smtpAccount/from, multi-recipient to, and base64 attachments
  • Worker retry logic and latency metrics (queueLatencyMs, dispatchLatencyMs)
  • JWT auth (/auth/token + Bearer on /send) and rate limiting
  • Dual log persistence:
    • SQLite (LOG_DB_PATH)
    • JSON line file (LOG_DIR/LOG_FILE_NAME)
  • CLI log dashboard:
    • npm run log mailsender
    • npm run log:mailsender
  • Legacy web monitor at http://localhost:8080 with SMTP account filtering
  • SMTP account management at http://localhost:8080/smtp
  • Encrypted application settings management at http://localhost:8080/settings

Current Enterprise Scope

MailFastApi now includes the core repository controls required for a larger transactional and marketing email platform:

Capability Current status
Multi SMTP accounts Encrypted SQLite vault, account selection by smtpAccount or sender address
Split API/worker runtime `MAILFASTAPI_ROLE=api
Durable production queue Redis backend required by production guard
Local/dev queue Memory queue remains available for development and isolated tests
Idempotency Tenant/actor scoped idempotency records prevent duplicate queue writes
Retry and DLQ Worker retry, lifecycle states, and dead-letter persistence
Failed-mail replay DLQ listing and authenticated retry back into the queue
Automatic failed retry Worker scans pending DLQ records and requeues them up to DLQ_AUTO_RETRY_MAX_ATTEMPTS
Suppression Global and tenant-level suppression for bounces, complaints, and unsubscribes
Deliverability SPF/DKIM/DMARC/MX/MTA-STS/TLS-RPT diagnostics and optional DKIM signing
Security JWT/API-key auth, production guard, CSRF, CSP nonce, session hardening, RBAC
Application settings Encrypted secure-store overrides for runtime, queue, auth, web, monitor, delivery, DKIM, logging, and updater settings
MFA Local TOTP can be enabled; development .env can disable it while production guard enforces safer settings
Updates Node.js updater with fast-forward flow, rollback, progress reporting, and signed-tag mode
Observability Health, Prometheus metrics, delivery events, queue depth, worker state, and legacy monitor

External production controls are still required for a real enterprise deployment: private network or VPN access for the admin panel, TLS termination, KMS/Vault or OS secret delivery, owned DNS records, provider feedback-loop enrollment, signed release keys, Prometheus/Grafana deployment, and production-grade Redis persistence/failover.

Project Structure

mailFastApi/
|-- src/
|   |-- app.js
|   |-- web.js
|   |-- auth.js
|   |-- mailQueueFactory.js
|   |-- memoryMailQueue.js
|   |-- redisMailQueue.js
|   |-- mailer.js
|   |-- operationalStore.js
|   |-- productionGuard.js
|   |-- domainHealth.js
|   |-- deliveryPolicy.js
|   |-- bounceClassifier.js
|   |-- dkimConfig.js
|   |-- secureStore.js
|   |-- webAuth.js
|   |-- queue.js
|   |-- worker.js
|   |-- systemLogger.js
|   `-- systemStore.js
|-- scripts/
|   |-- log-cli.js
|   `-- updater.js
|-- docs/
|   |-- API_DOCS.md
|   |-- ENTERPRISE_HARDENING.md
|   |-- TEST_REPORT.md
|   `-- assets/
|-- Tests/
|-- updater.sh
|-- .env.example
`-- package.json

Environment

See .env.example for full reference.

Important variables:

  • Queue:
    • QUEUE_BACKEND=redis
    • REDIS_URL=redis://127.0.0.1:6379
    • REDIS_QUEUE_KEY=mailfastapi:mail_jobs
    • QUEUE_VISIBILITY_TIMEOUT_MS=300000
    • QUEUE_RECLAIM_INTERVAL_MS=30000
  • Runtime role:
    • PRODUCTION_MODE=false|true
    • MAILFASTAPI_ROLE=all|api|worker
  • Logs:
    • LOG_DB_PATH=data/mailfastapi.sqlite
    • OPERATIONAL_DB_PATH=data/mailfastapi-operational.sqlite
    • LOG_DIR=logs
    • LOG_FILE_NAME=system.log
  • Secure store:
    • SECURE_STORE_KEY encrypts/decrypts data/mailfastapi-secure.sqlite
    • SECURE_STORE_KEY_FILE can point to a mounted secret file instead of storing the key in .env
    • SMTP host/user/password/from values are managed from the web panel, not .env
  • Send payload controls:
    • REQUEST_BODY_LIMIT (default 10mb)
    • MAX_ATTACHMENTS (default 10)
    • MAX_ATTACHMENT_TOTAL_BYTES (default 8388608)
  • Live monitor:
    • Core service: MONITOR_ENABLED, MONITOR_UI_ENABLED, MONITOR_PATH, METRICS_PATH
    • MONITOR_SSE_INTERVAL_MS, MONITOR_TOKEN
    • MONITOR_MAX_RECENT_ENTRIES, MONITOR_MAX_TIMELINE_MINUTES
  • Web service:
    • fixed legacy panel port: 8080
    • WEB_HOST, WEB_CORE_BASE_URL
    • WEB_MFA_REQUIRED=false for development, true is required in production mode
    • WEB_SESSION_IDLE_TIMEOUT_MS, WEB_SESSION_ABSOLUTE_TIMEOUT_MS
    • WEB_ENABLE_UPDATER, WEB_UPDATE_SCRIPT, WEB_UPDATE_TIMEOUT_MS
    • WEB_UPDATE_TOKEN (optional x-update-token header protection for update endpoints)
  • Updater:
    • UPDATER_RELEASE_MODE=branch|tag
    • UPDATER_ALLOWED_TAG_PATTERN and UPDATER_REQUIRE_SIGNED_TAG
    • UPDATER_NPM_BIN, UPDATER_RUN_TESTS, UPDATER_HEALTH_TIMEOUT_MS, UPDATER_LOCK_STALE_MS
  • Delivery safety:
    • IDEMPOTENCY_ENABLED, IDEMPOTENCY_HEADER, IDEMPOTENCY_TTL_MS
    • SUPPRESSION_ENABLED, SUPPRESSION_APPLIES_TO
    • PUBLIC_BASE_URL, UNSUBSCRIBE_SECRET
    • BOUNCE_WEBHOOK_ENABLED, BOUNCE_WEBHOOK_TOKEN, BOUNCE_DOMAIN
    • DELIVERY_POLICY_ENABLED, DOMAIN_POLICIES_JSON, SMTP_ACCOUNT_POLICIES_JSON
    • DKIM_SIGNING_ENABLED, DKIM_DOMAIN, DKIM_SELECTOR, DKIM_PRIVATE_KEY_PATH
    • DOMAIN_HEALTH_DKIM_SELECTORS

Run

Requires Node.js >=22.5.0 because the encrypted vault uses the built-in node:sqlite module.

npm install
npm start core
npm start web

Core URL (default): http://localhost:3000

Web panel URL (fixed): http://localhost:8080

Start targets:

  • npm start or npm start core: core service using the configured MAILFASTAPI_ROLE
  • npm start web: legacy web panel on fixed port 8080
  • npm start api: core service with MAILFASTAPI_ROLE=api
  • npm start worker: worker process with MAILFASTAPI_ROLE=worker
  • npm start all: core service with API and worker roles

On first web panel access, create the admin password. Then open SMTP Accounts and add accounts such as 2fa, info@example.com, or Bilgi Maili. Runtime mail delivery reads these accounts from the encrypted SQLite vault.

Prometheus metrics proxy URL (requires web login): http://localhost:8080/metrics

Encrypted settings URL (requires admin login): http://localhost:8080/settings

Settings saved from this page are stored in the encrypted SQLite secure store and applied when the relevant core or web process starts. Secret values are write-only in the browser; existing secret values are shown only as masked status.

Formatted monitor pages:

  • Metrics view: http://localhost:8080/metrics-view
  • Raw snapshot view: http://localhost:8080/raw-view

Cross-Platform Installer

Project root includes a cross-platform installer entrypoint:

  • installer.sh detects Linux, macOS, or Windows Git Bash/MSYS/Cygwin and dispatches to the correct platform installer.
  • install.sh is the Linux systemd installer.
  • macosinstaller.sh is the macOS launchd installer.
  • install.ps1 and installer.cmd are native Windows installers.

Linux registers two systemd services:

  • mailfastapi-core.service
  • mailfastapi-web.service

macOS registers two LaunchAgents:

  • com.mailfastapi.core
  • com.mailfastapi.web

Windows registers two Scheduled Tasks:

  • mailfastapi-core
  • mailfastapi-web

All installers:

  • creates/updates .env from .env.example
  • generates SECURE_STORE_KEY and MONITOR_TOKEN if missing/default
  • appends missing core/web settings
  • checks core/web ports
  • creates runtime directories and permissions
  • installs npm dependencies
  • runs Node syntax checks
  • enables and starts the platform service/task pair unless skipped

Windows note: if Redis is not detected and .env is newly created, the Windows installer sets QUEUE_BACKEND=memory for first-run compatibility. For production, install a Redis-compatible service and switch it back to redis.

Run installer:

chmod +x installer.sh
./installer.sh

Native Windows:

.\install.ps1
# or
.\installer.cmd

Common options:

./installer.sh --service-user mailer
./installer.sh --app-dir /opt/mailFastApi
./installer.sh --skip-system-deps
./installer.sh --skip-service
./installer.sh --skip-npm

Installer output includes a colored ASCII banner and project GitHub link.

Updater

The real updater is scripts/updater.js; updater.sh is a compatibility wrapper. It applies updates with a locked, fast-forward-only flow, syncs dependencies, runs syntax checks, restarts services/tasks, runs health checks, and rolls back to the previous commit if a post-merge step fails.

npm run update:check
npm run update:apply

# Compatibility wrapper
./updater.sh --check
./updater.sh --apply --yes

Release modes:

  • UPDATER_RELEASE_MODE=branch updates from the configured upstream branch.
  • UPDATER_RELEASE_MODE=tag updates to the latest tag matching UPDATER_ALLOWED_TAG_PATTERN.
  • Set UPDATER_REQUIRE_SIGNED_TAG=true in tag mode to require signed annotated tags.

The web monitor links to the legacy Update Control screen. That screen checks updates without browser confirm/alert prompts and shows updater steps with a progress bar. If a service account cannot find npm, set UPDATER_NPM_BIN or let the updater use the npm-cli.js bundled next to the active Node.js runtime.

Tests

npm test

Latest local verification summary is available in:

2026-05-27 local report:

Check Result
npm test 17 suites, 73 tests, 72 passed, 0 failed, 1 skipped
Node syntax checks passed for core, settings, web, monitor, secure store, worker, updater
git diff --check passed
Web visual smoke Playwright screenshots captured under docs/assets/
Autocannon load smoke HTTP 202 responses recorded; no non-2xx status bucket reported; avg latency 7.15 ms
Overload dry-run current plan calculated as 150 attempts, 20 concurrency, no real mail sent

The load smoke test uses a temporary API-only instance with the worker disabled, so it validates queue acceptance and API overhead without sending real email.

Real SMTP test:

npm test mailsend

Load test templates:

k6 run Tests/load/k6-send.js
ACCESS_TOKEN=<JWT_TOKEN> npm run test:load:autocannon
npm run test:overload

Autocannon options:

  • BASE_URL=http://127.0.0.1:3000
  • ACCESS_TOKEN=<JWT_TOKEN>
  • CONNECTIONS=50
  • DURATION=30
  • OVERALL_RATE=100
  • TEST_TO=load@example.com

The autocannon helper writes a temporary HAR file so Authorization: Bearer <token> is passed without fragile shell quoting.

Real SMTP overload test:

# Plan only; no real mail is sent.
npm run test:overload

# Current local API limit profile: RATE_LIMIT_MAX=120, so default total is 150 attempts.
OVERLOAD_CONFIRM_REAL_SEND=true npm run test:overload

# Provider-owned SMTP stress profile. Raise RATE_LIMIT_MAX first if API limit should not be the bottleneck.
OVERLOAD_PROFILE=smtp-provider OVERLOAD_TOTAL=1000 OVERLOAD_CONCURRENCY=50 OVERLOAD_CONFIRM_REAL_SEND=true npm run test:overload

# Heavier payload test. This sends 1 MB HTML bodies through the MailFastApi endpoint.
OVERLOAD_PROFILE=smtp-provider OVERLOAD_TOTAL=1000 OVERLOAD_CONCURRENCY=100 OVERLOAD_HTML_MB=1 OVERLOAD_CONFIRM_REAL_SEND=true npm run test:overload

# Max-performance profile for provider-owned infrastructure.
OVERLOAD_PROFILE=max-performance OVERLOAD_TOTAL=2000 OVERLOAD_CONCURRENCY=150 OVERLOAD_HTML_MB=2 OVERLOAD_CONFIRM_REAL_SEND=true npm run test:overload

Overload script behavior:

  • Reads auth and TEST_MAIL_TO from .env.
  • Sends through POST /send, not directly through Nodemailer.
  • Uses OVERLOAD_SMTP_ACCOUNT/SMTP_ACCOUNT to force a specific encrypted SMTP account.
  • Uses OVERLOAD_HTML_MB or OVERLOAD_HTML_BYTES to control the per-message HTML payload size.
  • Defaults to dry-run until OVERLOAD_CONFIRM_REAL_SEND=true is set.
  • Writes a JSON report under logs/overload-report-*.json unless OVERLOAD_REPORT_PATH is set.
  • Masks the test recipient in console/report target summary.

With the current development .env, the first meaningful overload number is 150 attempts: RATE_LIMIT_MAX=120 per 60000ms, plus 25 percent headroom to verify 429 behavior. Because the SMTP provider is under your control, the second-stage provider stress number is 1000 attempts after increasing API rate limits enough that MailFastApi can queue the burst. The max-performance profile is intended only for controlled provider-owned infrastructure because MB-sized payloads can generate gigabytes of real mail traffic quickly.

Retry failed/dead-lettered mail:

# Validate pending failed jobs without queueing them.
npm run retry:failed -- --dry-run --limit 100

# Requeue the latest pending failed jobs.
npm run retry:failed -- --limit 100

# Retry specific DLQ ids. Use --force only after confirming the recipient should be retried.
npm run retry:failed -- --ids 12,13

The retry flow keeps the original DLQ row for audit and marks it with the new queued job id. Hard-bounce and suppressed recipients are skipped unless --force is used.

By default, workers also retry pending failed/dead-lettered jobs in the background:

  • DLQ_AUTO_RETRY_ENABLED=true
  • DLQ_AUTO_RETRY_MAX_ATTEMPTS=3
  • DLQ_AUTO_RETRY_INTERVAL_MS=30000
  • DLQ_AUTO_RETRY_BATCH_SIZE=100

After the configured automatic attempts are exhausted, the DLQ row is marked as a final error and remains available for audit/manual review. For aggressive local/provider-owned stress runs, set WORKER_RESOURCE_MODE=max and a high WORKER_CONCURRENCY; actual throughput is still bounded by CPU, network, SQLite/Redis, SMTP pool settings, and the SMTP provider.

Log Dashboard

After traffic exists, render CLI dashboard:

npm run log mailsender

Dashboard includes:

  • 24h totals (mail sent, mail failed, retries)
  • event/level distributions
  • SMTP latency stats
  • per-minute throughput graph
  • recent structured logs

API

Endpoint details are in:

Multi-account send example, after adding the account in the web panel:

curl -X POST http://localhost:3000/send \
  -H "Authorization: Bearer <JWT_TOKEN>" \
  -H "Content-Type: application/json" \
  -d "{\"smtpAccount\":\"2fa\",\"to\":\"user@example.com\",\"subject\":\"Kod\",\"html\":\"<p>123456</p>\"}"

GPL Community Distribution

Before publishing a public release:

  • Keep LICENSE as GNU GPL v3.0.
  • Keep .env, data/, logs/, and secure SQLite vaults out of git.
  • Publish .env.example, docs, tests, and screenshots so users can reproduce the setup.
  • Prefer signed git tags for releases.
  • Run npm test, syntax checks, and at least one load smoke before tagging.
  • Do not publish real SMTP credentials, private DKIM keys, monitor tokens, updater tokens, or secure-store keys.

Recommended public release flow:

npm test
node --check src/app.js
node --check src/web.js
node --check src/monitor.js
node --check src/secureStore.js
node --check src/worker.js
node --check scripts/updater.js
git diff --check
git tag -s vX.Y.Z -m "MailFastApi vX.Y.Z"
git push origin main --tags

Contributing

Community contributions should include tests for behavior changes and documentation for operational or security-sensitive features. For mail-delivery changes, include the expected impact on queueing, idempotency, suppression, rate limiting, and deliverability.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages