> For the complete documentation index, see [llms.txt](https://docs.openalgo.in/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openalgo.in/developers/design-documentation/47-smtp-configuration.md).

# 47 - SMTP Configuration

## Storage

SMTP settings are columns on the single `Settings` row (`__tablename__ = "settings"`) in `database/settings_db.py`; there is no separate SMTP database or email service module.

| Column                    | Type        | Notes                                       |
| ------------------------- | ----------- | ------------------------------------------- |
| `smtp_server`             | String(255) | Hostname                                    |
| `smtp_port`               | Integer     | Route default is 587 when the form omits it |
| `smtp_username`           | String(255) |                                             |
| `smtp_password_encrypted` | Text        | Fernet ciphertext, never plaintext          |
| `smtp_use_tls`            | Boolean     | Defaults to `True`                          |
| `smtp_from_email`         | String(255) |                                             |
| `smtp_helo_hostname`      | String(255) | Optional EHLO/HELO override                 |

New password writes use `_get_smtp_fernet()`: PBKDF2-HMAC-SHA256, 100,000 iterations, keyed on the validated `API_KEY_PEPPER` with the salt from `SMTP_KEY_SALT` (env var, default `smtp-openalgo-salt`). `_decrypt_password()` falls back to `_legacy_smtp_fernet()` (the raw pepper padded to 32 bytes, no KDF) for values written before that upgrade, and re-saving the settings migrates them forward. Decrypted passwords must never be returned by profile/admin APIs or logged.

## Authenticated Routes

`blueprints/auth.py` exposes:

| Method/path              | Purpose                                                                          |
| ------------------------ | -------------------------------------------------------------------------------- |
| `POST /auth/smtp-config` | Update settings from form fields; a blank password preserves the existing secret |
| `POST /auth/test-smtp`   | Validate recipient and send a test message                                       |
| `POST /auth/debug-smtp`  | Run connection/auth diagnostics                                                  |
| `GET /auth/profile-data` | Return non-secret config and password-present state                              |

The React configuration UI is the SMTP tab in `frontend/src/pages/Profile.tsx`.

## Delivery

`utils/email_utils.py` exposes `send_test_email()`, `send_password_reset_email()`, `send_email()`, and `validate_smtp_settings()`. Port 465 uses `smtplib.SMTP_SSL` from the start; any other port opens a plain `SMTP` connection and upgrades with `starttls()` when `smtp_use_tls` is set. `smtp_helo_hostname` is passed to `ehlo()`, falling back to `smtp_server` when it is blank. `utils/email_debug.py` performs staged diagnostics behind `POST /auth/debug-smtp`.

## Security Rules

* Require a valid app session and CSRF token for changes.
* Preserve the existing password when the form leaves it blank.
* Return password presence only, never ciphertext or plaintext.
* Do not include credentials in SMTP errors or logs.
* Prefer provider app passwords and TLS/SSL.
* Treat debug output as authenticated operational data.

## Key Files

| File                             | Purpose                      |
| -------------------------------- | ---------------------------- |
| `database/settings_db.py`        | Settings and SMTP encryption |
| `utils/email_utils.py`           | Test/reset/general delivery  |
| `utils/email_debug.py`           | Connection diagnostics       |
| `blueprints/auth.py`             | Configuration routes         |
| `frontend/src/pages/Profile.tsx` | SMTP form                    |
