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.rsstarts the Actix‑Web server, loading configuration frombackend/backend_config.tomland environment variables (.env.example). - HTTP Layer:
backend/src/http/mod.rsregisters routes. ThePOST /v0/check_emailhandler (backend/src/http/v0/check_email/post.rs) parses the request, builds aCheckEmailInput, and callscore::check_email. - Core Logic:
core/src/lib.rscallscore::smtp::connect(viacore/src/smtp/*.rs), which selects a provider (Gmail, Outlook, Yahoo, etc.) and runs the verification flow. DNS queries usetrust-dns-resolver, and the SMTP handshake is performed withasync-std‑based sockets. - Persistence: Results are written through
backend/src/storage/postgres.rs. Migrations live inbackend/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 viabackend/src/http/v1/bulk/get_progress.rs. - CLI:
cli/src/main.rssimply forwards arguments tocore::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/, andCargo.lockexist; CI is GitHub Actions. - Analysis: A static analysis run reports no structural red flags.
- Coverage: 5 test files under
backend/tests/andcli/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.