Sending Mail via SMTP
rmail accepts authenticated SMTP on port 587 (submission) and unauthenticated inbound delivery on port 25. Implicit TLS (SMTPS) is available on port 465 when TLS is configured.
Python (with STARTTLS)
import smtplib, ssl
server = smtplib.SMTP("localhost", 587)
server.ehlo("client.example.com")
server.starttls(context=ssl.create_default_context())
server.ehlo("client.example.com")
server.login("alice@example.com", "password")
server.sendmail(
"alice@example.com",
["bob@example.com"],
"From: alice@example.com\r\nTo: bob@example.com\r\nSubject: Hello\r\n\r\nBody\r\n"
)
server.quit()
Python (implicit TLS on port 465)
import smtplib, ssl
ctx = ssl.create_default_context()
with smtplib.SMTP_SSL("localhost", 465, context=ctx) as server:
server.login("alice@example.com", "password")
server.sendmail(
"alice@example.com",
["bob@example.com"],
"From: alice@example.com\r\nTo: bob@example.com\r\nSubject: Hello\r\n\r\nBody\r\n"
)
SMTP EHLO Feature Behaviour
When TLS is configured, the EHLO response on cleartext connections advertises STARTTLS but not AUTH. After STARTTLS negotiation, AUTH PLAIN LOGIN becomes available. On connections without TLS configuration, AUTH is always advertised.
Reading Mail via IMAP
IMAP4rev1 is available on port 143 (plain/STARTTLS) and port 993 (implicit TLS).
Python
import imaplib
imap = imaplib.IMAP4("localhost", 143)
imap.login("alice@example.com", "password")
imap.select("INBOX")
status, data = imap.search(None, "ALL")
for num in data[0].split():
status, msg = imap.fetch(num, "(BODY.PEEK[])")
print(msg)
imap.logout()
Supported IMAP Commands
CAPABILITY, LOGIN, AUTHENTICATE (PLAIN, LOGIN), STARTTLS, SELECT, EXAMINE, LIST, LSUB, SUBSCRIBE, UNSUBSCRIBE, STATUS, FETCH, STORE, APPEND, EXPUNGE, COPY, MOVE, UID, SEARCH, IDLE, ID, ENABLE, CHECK, UNSELECT, RENAME, CREATE, DELETE, NAMESPACE, NOOP, LOGOUT, CLOSE.
Supported SEARCH Criteria
ALL, UNSEEN, SEEN, FLAGGED, UNFLAGGED, ANSWERED, UNANSWERED, DELETED, UNDELETED, DRAFT, UNDRAFT, NEW, OLD, RECENT, UID, FROM, TO, SUBJECT, BODY, TEXT, HEADER, BEFORE, SINCE, ON, LARGER, SMALLER, NOT, OR.
IMAP Capabilities Advertised
Always: IMAP4rev1, IDLE, SPECIAL-USE, NAMESPACE, UIDPLUS, MOVE, ID, ENABLE, UNSELECT, LITERAL+. Conditionally: AUTH=PLAIN, AUTH=LOGIN (when TLS active or no TLS configured), STARTTLS (when TLS configured on cleartext connection).
Exchange REST API
All endpoints require a Bearer JWT token obtained via POST /auth/login. UIDs are scoped per folder. Tokens expire after 3600 seconds.
Authenticate
TOKEN=$(curl -s -X POST http://localhost:9002/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"alice","password":"password","domain":"example.com"}' \
| python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
List Folders
curl -s http://localhost:9002/folders \
-H "Authorization: Bearer $TOKEN"
List Messages (with pagination)
curl -s "http://localhost:9002/folders/INBOX/messages?offset=0&limit=25" \
-H "Authorization: Bearer $TOKEN"
Get a Message
curl -s http://localhost:9002/folders/INBOX/messages/1 \
-H "Authorization: Bearer $TOKEN"
Update Flags
curl -s -X PATCH http://localhost:9002/folders/INBOX/messages/1/flags \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"flags":["\\Seen","\\Flagged"]}'
Send a Message
curl -s -X POST http://localhost:9002/messages/send \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"to":["bob@example.com"],"subject":"Hello","body":"Test"}'
Delete a Message
curl -s -X DELETE http://localhost:9002/folders/INBOX/messages/1 \
-H "Authorization: Bearer $TOKEN"
Manage Aliases (admin only)
# Add an alias (admin token required)
curl -s -X POST http://localhost:9002/aliases \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"pattern":"sales-.*","target":"alice"}'
# Remove an alias by path (admin token required)
curl -s -X DELETE "http://localhost:9002/aliases/sales-.*" \
-H "Authorization: Bearer $ADMIN_TOKEN"
API Endpoint Reference
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /auth/login | None | Authenticate, returns JWT token (3600s expiry). |
| GET | /folders | User | List folders with message counts. |
| GET | /folders/{folder}/messages | User | List messages; supports offset and limit query params. |
| POST | /folders/{folder}/messages | User | Append a raw RFC822 message. |
| GET | /folders/{folder}/messages/{uid} | User | Retrieve a message by UID. |
| DELETE | /folders/{folder}/messages/{uid} | User | Delete a message. |
| PATCH | /folders/{folder}/messages/{uid}/flags | User | Replace the flag set on a message. |
| POST | /messages/send | User | Compose and deliver a message. |
| GET | /aliases | User | List aliases for the authenticated domain. |
| POST | /aliases | Admin | Add an alias rule. |
| DELETE | /aliases | Admin | Remove an alias rule (pattern in request body). |
| DELETE | /aliases/{pattern} | Admin | Remove an alias rule (pattern in URL path). |
| OPTIONS | * | None | CORS preflight; returns 204 with CORS headers. |
| GET | /.well-known/autoconfig/mail/config-v1.1.xml | None | Thunderbird autoconfig XML. |
| POST | /autodiscover/autodiscover.xml | None | Outlook autodiscover XML. |
Alias Management
Aliases are per-domain regex rules that route addresses to existing user accounts. Exact user lookup takes priority; pattern rules are evaluated in declaration order.
python run.py alias list example.com
python run.py alias add example.com ".*" alice # catch-all
python run.py alias add example.com "sales-.*" alice # prefix match
python run.py alias remove example.com "sales-.*"
Alias rules can also be managed via Makefile targets:
make alias
make delalias
make listaliases
Terminal Client
rmail includes a curses-based IMAP/SMTP client for browsing, reading, composing, and replying to mail:
python -m rmail.client
Or using the Makefile shortcut:
make client
Running Tests and Diagnostics
python3 run.py test
Runs the end-to-end test suite against a live server instance.
Diagnostics
sudo python3 run.py diagnose
Executes an 8-phase diagnostic sequence: configuration, user management, backend storage, SMTP protocol, delivery verification, IMAP protocol, Exchange API, and cleanup. A healthy server produces 0 failures and 0 warnings.