API Setup Guide

Node.js + Express API to manage aliases in the Base Stack MariaDB

This project (maintained by a member of Haltman.io) is a mail forwarding service API built with Node.js and Express. It exposes HTTP endpoints that allow users to:

  • request alias creation (subscribe)

  • confirm alias creation via email token (confirm)

  • request alias removal (unsubscribe)

  • confirm alias removal via email token (unsubscribe confirm)

Key points:

  • This API does not receive emails.

  • It only manages forwarding rules (aliases) by writing directly into the same MariaDB database used by the Base Stack (Postfix + MariaDB).

  • All operations are rate-limited, abuse-resistant, and confirmed via email tokens.


Important prerequisite (mandatory)

This API does not work standalone.

Before deploying this service, you must deploy the Base Stack first:

  • Postfix + MariaDB schema (domains + aliases)

  • PostSRSd (SRS)

Read first:


Installation

Requirements

  • Node.js (current LTS recommended)

  • A running Base Stack (MariaDB reachable with correct schema)

  • Working SMTP credentials (required to send confirmation emails)

  • Redis (optional, recommended for production rate limiting)

Install

git clone https://github.com/haltman-io/mail-forwarding.git
cd ./mail-forwarding/app
npm install

Configuration

Create .env

This project does not ship with a .env file.

cp .env.example .env

If .env.example is not present, create .env manually.

Minimal required .env settings

Application

APP_ENV=prod
APP_PORT=3000
TRUST_PROXY=1
APP_PUBLIC_URL=https://api.example.org
EMAIL_CONFIRM_CONFIRM_ENDPOINT=/forward/confirm

Notes:

  • TRUST_PROXY must reflect how many reverse proxies are in front of the API (affects IP-based rate limiting).

  • APP_PUBLIC_URL is used to build confirmation links.

MariaDB (Base Stack database)

MARIADB_HOST=127.0.0.1
MARIADB_PORT=3306
MARIADB_USER=...
MARIADB_PASSWORD=...
MARIADB_DATABASE=...

These must point to the same database used by the Base Stack.

SMTP (required)

SMTP_HOST=...
SMTP_PORT=587
SMTP_SECURE=false
SMTP_AUTH_ENABLED=true
SMTP_USER=...
SMTP_PASS=...
SMTP_FROM="Mail Forwarding <no-reply@example.org>"
SMTP_TLS_REJECT_UNAUTHORIZED=true

Without SMTP, confirmation emails will not be sent, and the service is effectively unusable.

Tokens (minimum)

EMAIL_CONFIRMATION_TTL_MINUTES=10
EMAIL_CONFIRMATION_RESEND_COOLDOWN_SECONDS=60
EMAIL_CONFIRMATION_TOKEN_LEN=12
DEFAULT_ALIAS_DOMAIN=example.org

Used when the user does not pass ?domain=.

REDIS_URL=redis://127.0.0.1:6379
REDIS_RATE_LIMIT_PREFIX=rl:
REDIS_CONNECT_TIMEOUT_MS=5000

If REDIS_URL is empty, rate limiting uses in-memory storage (not shared across instances).


Running

node ./source/server.js

(Optional) using PM2:

pm2 start ./source/server.js --name mail-forwarding-api --no-daemon

How to use (HTTP endpoints)

Important rules:

  • All endpoints are GET-only

  • All inputs are query parameters

  • No JSON body / POST is used anywhere

1) Create alias request

GET /forward/subscribe

Example:

/forward/subscribe?name=github&to=user@gmail.com&domain=example.org

Parameters:

  • name (required): alias local-part

  • to (required): destination mailbox

  • domain (optional): alias domain (defaults to DEFAULT_ALIAS_DOMAIN)

Expected responses (common):

  • 200 ok (confirmation email sent)

  • 400 invalid_input

  • 409 alias_taken

  • 429 rate_limited

  • 403 banned


2) Confirm alias creation

GET /forward/confirm?token=...

Example:

/forward/confirm?token=AbC123xYz

Expected responses (common):

  • 200 confirmed

  • 400 invalid_token

  • 404 token_not_found

  • 410 token_expired

  • 429 rate_limited


3) Remove alias request

GET /forward/unsubscribe

Example:

/forward/unsubscribe?address=github@example.org

Parameters:

  • address (required): full alias address

Expected responses (common):

  • 200 ok (confirmation email sent)

  • 404 not_found

  • 429 rate_limited

  • 403 banned


4) Confirm alias removal

GET /forward/unsubscribe/confirm?token=...

Example:

/forward/unsubscribe/confirm?token=ZyX987Ab

Expected responses (common):

  • 200 removed

  • 400 invalid_token

  • 404 token_not_found

  • 410 token_expired

  • 429 rate_limited


Possible problems / Important notes

  • Base Stack not deployed: API may start, but operations will fail (missing schema / wrong DB).

  • SMTP misconfigured: subscribe/unsubscribe will not complete (no confirmation emails).

  • Behind proxy without TRUST_PROXY: rate limiting may treat all users as the same IP.

  • No Redis in production: multi-instance deployments will have inconsistent rate limiting.

  • Destructive behavior: this service writes directly into mail routing tables. Test in staging first.

Updated on