The Problem

Organizations need to verify that an e‑mail address is deliverable without actually sending a message. Existing solutions either send a test message or require a paid API. This repo implements a self‑hostable verification engine that checks SMTP connectivity, MX records, and various provider‑specific heuristics, all without mail delivery.

What This Does

The library check-if-email-exists exposes the async function check_email, defined in core/src/lib.rs. It orchestrates DNS look‑ups, SMTP handshakes, and provider‑specific checks, returning a Vec<CheckEmailOutput> that includes status, provider, and any error details. The HTTP backend (see backend/src/main.rs and backend/src/http/mod.rs) exposes a /v0/check_email POST endpoint that accepts a JSON body with to_email and an optional proxy. The backend stores results in a PostgreSQL database (backend/src/storage/postgres.rs) and supports bulk jobs (backend/src/http/v0/bulk/*). A lightweight CLI (cli/src/main.rs) bundles the library and offers a local, no‑backend verification mode.

How It Is Wired

  • Entry Point: backend/src/main.rs starts the Actix‑Web server, loading configuration from backend/backend_config.toml and environment variables (.env.example).
  • HTTP Layer: backend/src/http/mod.rs registers routes. The POST /v0/check_email handler (backend/src/http/v0/check_email/post.rs) parses the request, builds a CheckEmailInput, and calls core::check_email.
  • Core Logic: core/src/lib.rs calls core::smtp::connect (via core/src/smtp/*.rs), which selects a provider (Gmail, Outlook, Yahoo, etc.) and runs the verification flow. DNS queries use trust-dns-resolver, and the SMTP handshake is performed with async-std‑based sockets.
  • Persistence: Results are written through backend/src/storage/postgres.rs. Migrations live in backend/migrations/.
  • Background Work: For bulk requests, the worker spawn logic in backend/src/worker/* reads a queue (PostgreSQL + optional RabbitMQ) and processes each email, exposing progress via backend/src/http/v1/bulk/get_progress.rs.
  • CLI: cli/src/main.rs simply forwards arguments to core::check_email, bypassing the HTTP layer.

The call graph is linear: main.rs → HTTP handler → core::check_email → provider‑specific SMTP module → postgres.rs. The only side‑effects outside the process are database writes and external SMTP connections.

How To Use It

Setup

# Clone the repo
git clone https://github.com/moses-y/check-if-email-exists
cd check-if-email-exists

# Build the backend Docker image
docker build -t reacherhq/backend -f backend/Dockerfile .

Configuration Copy the example files:

cp backend/.env.example backend/.env
cp backend/backend_config.toml backend/config.toml

Set DATABASE_URL and optional SMTP_* or PROXY_* variables in .env.

Run the backend

docker run -p 8080:8080 -v $(pwd)/backend/.env:/app/.env reacherhq/backend

CLI usage

cargo build --release --bin check-if-email-exists
./target/release/check-if-email-exists someone@example.com

Programmatic

use check_if_email_exists::{check_email, CheckEmailInput};

let input = CheckEmailInput::new(vec!["someone@example.com".into()]);
let result = check_email(&input).await?;

Real-World Use

A marketing platform can expose a micro‑service endpoint that forwards user‑submitted addresses to the /v0/check_email route, stores the result in a PostgreSQL table, and flags addresses that failed for later review. The bulk API allows nightly jobs to pre‑validate large address lists.

Code Health & Issues

  • Structure: tests/, .github/workflows/, and Cargo.lock exist; CI is GitHub Actions.
  • Analysis: A static analysis run reports no structural red flags.
  • Coverage: 5 test files under backend/tests/ and cli/tests/.
  • Licensing: AGPL‑3.0 in LICENSE.AGPL.
  • Missing: No integration tests for the SMTP provider modules; documentation for environment variables is limited to the README.

The Bottom Line

check-if-email-exists offers a self‑hostable, Rust‑based solution for e‑mail existence verification. The library and backend are modular, with clear boundaries between HTTP, core logic, and persistence. It is ready for production use in environments that can run a PostgreSQL database and allow outbound SMTP. The main trade‑off is the requirement for open port 25 and a working SMTP provider; the code base is well‑structured but could benefit from more comprehensive provider‑specific tests.