{"id":30067107,"url":"https://github.com/mbalatsko/emailverifier-kt","last_synced_at":"2025-08-08T08:08:04.347Z","repository":{"id":302083179,"uuid":"1008256672","full_name":"mbalatsko/emailverifier-kt","owner":"mbalatsko","description":"EmailVerifier is a composable, pluggable Kotlin library for validating email addresses beyond just their syntax. It's built with a clear focus: help developers reliably assess whether a given email is real, meaningful, and worth accepting.","archived":false,"fork":false,"pushed_at":"2025-07-29T11:23:48.000Z","size":1528,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-29T12:38:44.703Z","etag":null,"topics":["disposable-email","email","email-validation","kotlin","validation","verification"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mbalatsko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-25T09:09:55.000Z","updated_at":"2025-07-29T12:17:21.000Z","dependencies_parsed_at":"2025-07-14T23:09:25.949Z","dependency_job_id":"c0523184-0854-4b47-8cb5-a705d02bd3b4","html_url":"https://github.com/mbalatsko/emailverifier-kt","commit_stats":null,"previous_names":["mbalatsko/emailverifier-kt"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/mbalatsko/emailverifier-kt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbalatsko%2Femailverifier-kt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbalatsko%2Femailverifier-kt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbalatsko%2Femailverifier-kt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbalatsko%2Femailverifier-kt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mbalatsko","download_url":"https://codeload.github.com/mbalatsko/emailverifier-kt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbalatsko%2Femailverifier-kt/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269385800,"owners_count":24408433,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-08T02:00:09.200Z","response_time":72,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["disposable-email","email","email-validation","kotlin","validation","verification"],"created_at":"2025-08-08T08:08:00.721Z","updated_at":"2025-08-08T08:08:04.329Z","avatar_url":"https://github.com/mbalatsko.png","language":"Kotlin","funding_links":[],"categories":["邮件库"],"sub_categories":["文件同步"],"readme":"# emailverifier-kt 📬\n\n[![Kotlin Docs](https://img.shields.io/badge/docs-kotlin-blue?logo=kotlin)](https://mbalatsko.github.io/emailverifier-kt/)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.mbalatsko/emailverifier-kt.svg?label=Maven%20Central)](https://search.maven.org/artifact/io.github.mbalatsko/emailverifier-kt)\n[![GitHub Packages](https://img.shields.io/badge/github-packages-blue)](https://github.com/mbalatsko/emailverifier-kt/packages)\n[![License: MIT](https://img.shields.io/github/license/mbalatsko/emailverifier-kt)](https://github.com/mbalatsko/emailverifier-kt/blob/main/LICENSE)\n[![Kotlin](https://img.shields.io/badge/Kotlin-2.1.20-blue.svg?logo=kotlin)](https://kotlinlang.org/)\n[![Latest Release](https://img.shields.io/github/release/mbalatsko/emailverifier-kt.svg)](https://github.com/mbalatsko/emailverifier-kt/releases)\n[![Platform](https://img.shields.io/badge/platform-jvm-blue)](#)\n\n\n**emailverifier-kt** is a composable, pluggable Kotlin library for validating email addresses beyond just their syntax. It's built with a clear focus: help developers **reliably assess whether a given email is real, meaningful, and worth accepting**.\n\n## ⚡️ Performance\n\n`emailverifier-kt` is designed for high performance and uses Kotlin's coroutines to parallelize I/O operations:\n\n- **Parallel Initialization:** All external data sources (e.g., Public Suffix List, disposable domains) are downloaded concurrently during setup, making initialization significantly faster.\n- **Parallel Verification:** Independent network checks (MX records, Gravatar) are executed concurrently for each email, reducing the verification time.\n\n## ✅ Features\n\n`emailverifier-kt` performs a layered set of validations:\n\n### 1. **Syntax Validation**\nChecks the structure of the email:\n- Local-part format (dot-atom and quoted-string, per RFC 5322 subset)\n- Hostname validity (RFC 1035, IDNA-compliant)\n\n### 2. **Registrability Check**\nVerifies whether the email domain is **registrable**:\n- Uses the [Public Suffix List](https://publicsuffix.org/)\n- Returns the registrable domain (e.g., `example.co.uk` for `user@example.co.uk`) or `Failed` if not registrable (e.g., `user@something.invalid`).\n\n### 3. **MX Record Lookup**\nEnsures the domain is actually configured to receive emails:\n- Queries DNS-over-HTTPS (DoH) via Google\n- Returns a list of MX records or `Failed` if no MX records are found.\n\n### 4. **Disposable Email Detection**\nFilters out **temporary/disposable** email domains:\n- Uses curated lists from [disposable-email-domains](https://github.com/disposable/disposable-email-domains)\n- Detects domains like `mailinator.com`, `tempmail.org`, etc.\n- You can also specify `allow` and `deny` sets to customize the behavior for specific domains.\n\n### 5. **Gravatar Existence Check**\n\nDetects whether an email has an associated **Gravatar**:\n- Computes MD5 hash of the email\n- Returns the Gravatar URL or `Failed` if no custom avatar is found.\n\n### 6. **Free Email Provider Detection**\nChecks whether the email domain belongs to a known free‐email provider (e.g. `gmail.com`, `yahoo.com`) \nusing a curated list of popular services.\n- Returns `Passed` result if email hostname is not a known free‐email provider\n- You can also specify `allow` and `deny` sets to customize the behavior for specific domains.\n\nList used: [Github gist](https://gist.github.com/okutbay/5b4974b70673dfdcc21c517632c1f984) by @okutbay \n\n### 7. **Role-Based Username Detection**\nDetects generic or departmental username (e.g. `info@`, `admin@`, `support@`) by checking against a curated list of common role-based usernames.\n- Returns `Passed` result if email username is not a known role-based username\n- You can also specify `allow` and `deny` sets to customize the behavior for specific usernames.\n\nList used: https://github.com/mbalatsko/role-based-email-addresses-list (original repo: https://github.com/mixmaxhq/role-based-email-addresses)\n\n### 8. **SMTP Deliverability Check**\nPerforms a live check with the mail server to verify if the mailbox actually exists.\n- Connects to the mail server and uses the `RCPT TO` command to check for deliverability without sending an email.\n- Can detect \"catch-all\" server configurations where all emails to a domain are accepted.\n- **Disabled by default**, as most cloud providers and ISPs block outbound traffic on port 25 to prevent spam. \nCan be enabled and configured to work through a SOCKS proxy.\n\n### 9. **Offline Mode**\nFor environments without internet access, `EmailVerifier` can run in a fully **offline** mode. When enabled, it uses bundled \ndata for checks that support it (Syntax, Registrability, Disposability, Free Email, and Role-Based Username) and automatically \ndisables checks that require network access (MX Record, Gravatar, SMTP). \n\nYou can also configure **offline mode** for each check **individually**.\n\nThe bundled data is manually updated **before release** via a GitHub Actions workflow.\n\n## 🧪 Output: Validation Results\n\nYou get a detailed result for each check:\n\n```kotlin\ndata class EmailValidationResult(\n    val email: String,\n    val emailParts: EmailParts,\n    val syntax: CheckResult\u003cSyntaxValidationData\u003e,\n    val registrability: CheckResult\u003cRegistrabilityData\u003e,\n    val mx: CheckResult\u003cMxRecordData\u003e,\n    val disposable: CheckResult\u003cDatasetData\u003e,\n    val gravatar: CheckResult\u003cGravatarData\u003e,\n    val free: CheckResult\u003cDatasetData\u003e,\n    val roleBasedUsername: CheckResult\u003cDatasetData\u003e,\n    val smtp: CheckResult\u003cSmtpData\u003e,\n) {\n    /**\n     * Returns true if all strong indicator checks passed.\n     * Strong indicator checks are: syntax, registrability, mx record presence, and disposability.\n     * These checks are the most likely to indicate that an email address is not valid.\n     */\n    fun isLikelyDeliverable(): Boolean\n}\n\n/**\n * A sealed class representing the result of a single validation check.\n * It can be in one of four states: Passed, Failed, Skipped, or Errored.\n *\n * @param T the type of data carried by the result.\n */\nsealed class CheckResult\u003cout T\u003e {\n    /**\n     * Indicates that the check was successful.\n     * @property data data associated with the passed check.\n     */\n    data class Passed\u003cT\u003e(\n        val data: T,\n    ) : CheckResult\u003cT\u003e()\n\n    /**\n     * Indicates that the check failed.\n     * @property data optional data associated with the failed check.\n     */\n    data class Failed\u003cT\u003e(\n        val data: T? = null,\n    ) : CheckResult\u003cT\u003e()\n\n    /**\n     * Indicates that the check was skipped.\n     */\n    data object Skipped : CheckResult\u003cNothing\u003e()\n\n    /**\n     * Indicates that the check produced an error.\n     * @property error the throwable that was caught during the check.\n     */\n    data class Errored(\n        val error: Throwable,\n    ) : CheckResult\u003cNothing\u003e()\n}\n\n/**\n * Data class holding the validity of each part of the email syntax.\n * @property username true if the username part is valid.\n * @property plusTag true if the plus-tag part is valid.\n * @property hostname true if the hostname part is valid.\n */\ndata class SyntaxValidationData(\n    val username: Boolean,\n    val plusTag: Boolean,\n    val hostname: Boolean,\n)\n\n/**\n * Data class holding the registrable domain found during the registrability check.\n * @property registrableDomain The registrable domain string, or null if not found.\n */\ndata class RegistrabilityData(\n    val registrableDomain: String?,\n)\n\n/**\n * Data class holding the MX records found during the MX record check.\n * @property records A list of [MxRecord]s, or an empty list if none were found.\n */\ndata class MxRecordData(\n    val records: List\u003cMxRecord\u003e,\n)\n\n/**\n * Data class holding the Gravatar URL found during the Gravatar check.\n * @property gravatarUrl The Gravatar URL string, or null if no custom avatar was found.\n */\ndata class GravatarData(\n    val gravatarUrl: String?,\n)\n\n/**\n * Data class holding the result of a dataset check (disposable, free, role-based).\n *\n * @property match true if a match was found in the dataset.\n * @property matchedOn the specific entry that was matched, or null if no match was found.\n * @property source the source of the match (e.g., \"allow\", \"deny\", \"default\").\n */\ndata class DatasetData(\n    val match: Boolean,\n    val matchedOn: String? = null,\n    val source: Source? = null,\n)\n\n/**\n * Data class holding the results of an SMTP check.\n *\n * @property isDeliverable true if the email address is deliverable.\n * @property isCatchAll true if the server has a catch-all policy, false if not, null if inconclusive.\n * @property smtpCode the last SMTP response code.\n * @property smtpMessage the last SMTP response message.\n */\ndata class SmtpData(\n    val isDeliverable: Boolean,\n    val isCatchAll: Boolean?,\n    val smtpCode: Int,\n    val smtpMessage: String,\n)\n```\n\nEach check can return:\n- `Passed` ✅ (with optional data, see data classes above for details)\n- `Failed` ❌ (with optional data, see data classes above for details)\n- `Errored` ⚠️ (if an unexpected error occurred during the check)\n- `Skipped` ⏭️ (if not enabled or not applicable)\n\nFor `Disposable Email Detection`, `Free Email Provider Detection`, and `Role-Based Username Detection`, the result is a `CheckResult\u003cDatasetData\u003e`. The `Passed` state indicates the email is *not* disposable/free/role-based, while `Failed` indicates it *is*. The `DatasetData` object provides more context, including the specific rule or entry that was matched.\n\n## 🚀 Getting Started\n\n### 1. Add dependency (JVM only for now)\n\nMaven:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.mbalatsko\u003c/groupId\u003e\n    \u003cartifactId\u003eemailverifier-kt\u003c/artifactId\u003e\n    \u003cversion\u003eLATEST_VERSION\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nGradle:\n\n```groovy\nimplementation(\"io.github.mbalatsko:emailverifier-kt:LATEST_VERSION\")\n```\n\nAlso available on [Github Packages](https://github.com/mbalatsko/emailverifier-kt/packages/2563296)\n\n### 2. Basic usage\n\n```kotlin\nval verifier =  emailVerifier { }\n\nval result = verifier.verify(\"john.doe@example.com\")\n\nif (result.isLikelyDeliverable()) {\n    println(\"Valid email!\")\n} else {\n    println(\"Email validation failed: $result\")\n}\n```\n\n### 3. Customizing Checks\n\nAll checks are enabled by default, but you can easily disable or configure them.\n\n```kotlin\nval verifier = emailVerifier {\n    // Disable a check\n    mxRecord {\n        enabled = false\n    }\n\n    registrability {\n        customRules = setOf(\n          \"my-private-tld\",       // Treat .my-private-tld as a public suffix\n          \"*.my-private-domain\",  // Treat all subdomains of .my-private-domain as public suffixes\n          \"!example.my-private-domain\" // Make an exception to the wildcard rule\n        )\n    }\n\n    // Configure allow/deny lists for dataset checks\n    disposability {\n        allow = setOf(\"my-disposable-domain.com\") // Whitelist a disposable domain\n        deny = setOf(\"my-domain.com\")             // Blacklist a domain\n    }\n\n    // Configure SMTP parameters\n    smtp {\n        enabled = true // IMPORTANT: Disabled by default. See notes below.\n        timeoutMillis = 10000 // Increase connection timeout\n    }\n}\n```\n\n\u003e **⚠️ Important Note on SMTP Checks**\n\u003e The SMTP check is **disabled by default** because most Internet Service Providers (ISPs) and cloud hosting providers (like AWS, GCP, Azure) block outgoing requests on port 25 to prevent email spamming.\n\u003e\n\u003e To perform this check reliably, you will likely need to route the connection through a **SOCKS proxy** that has unrestricted access to port 25.\n\u003e\n\u003e Here is how you can configure it:\n\u003e ```kotlin\n\u003e import java.net.InetSocketAddress\n\u003e import java.net.Proxy\n\u003e\n\u003e val verifier = emailVerifier {\n\u003e     smtp {\n\u003e         enabled = true\n\u003e         // Configure a SOCKS proxy\n\u003e         proxy = Proxy(Proxy.Type.SOCKS, InetSocketAddress(\"your-proxy-host.com\", 1080))\n\u003e     }\n\u003e }\n\u003e ```\n\n### 4. Configuring Data Sources\n\nFor checks that rely on external datasets (Registrability, Disposability, Free Email, and Role-Based Username), you have full control over the data source.\n\n#### Global Offline Mode\n\nFor ultimate convenience, you can set the global `allOffline` flag. This forces all checks to use their bundled offline data and disables checks that require a network connection (MX, Gravatar, SMTP). This is the simplest way to configure the verifier for an environment with no internet access.\n\n```kotlin\nval verifier = emailVerifier {\n    allOffline = true\n}\n\nval result = verifier.verify(\"mbalatsko@gmail.com\")\n// result.mx will be SKIPPED\n// result.gravatar will be SKIPPED\n// result.smtp will be SKIPPED\n```\n\n#### Per-Check Configuration\n\nYou can also configure the data source for each check individually.\n\n##### Using Default Offline Data\n\nThe `offline` property provides a simple toggle between the default remote URL and the default bundled data source for a specific check.\n\n```kotlin\nval verifier = emailVerifier {\n    // Use the bundled offline data for this check\n    registrability {\n        offline = true\n    }\n\n    // Use the online source for this one (default behavior)\n    disposability {\n        offline = false\n    }\n}\n```\n\n##### Using a Custom Data Source\n\nFor complete control, you can provide a custom data source using the `source` property. This is ideal for using proprietary lists, testing, or managing datasets locally. The `DataSource` type ensures your configuration is clear and type-safe.\n\n```kotlin\nimport io.github.mbalatsko.emailverifier.DataSource\n\nval verifier = emailVerifier {\n    // Use a custom remote URL\n    registrability {\n        source = DataSource.Remote(\"https://my.custom.domain/public_suffix_list.dat\")\n    }\n\n    // Use a custom file from your classpath resources\n    disposability {\n        source = DataSource.Resource(\"/my_disposable_domains.txt\")\n    }\n\n    // Use a custom file from the local filesystem\n    free {\n        source = DataSource.File(\"/path/to/your/free_emails.txt\")\n    }\n}\n```\n\n### 5. Advanced Configuration: Custom HttpClient\n\nThe default `HttpClient` used by `EmailVerifier` is configured with a sensible retry policy (`retryOnServerErrors(maxRetries = 3)` with exponential backoff) to handle transient network issues.\n\nFor more advanced use cases, such as adding custom headers or using a different engine, you can pass a custom-configured `HttpClient` to the `EmailVerifier`. This gives you full control over the network layer.\n\nHere's an example of how to configure a custom client:\n\n```kotlin\nimport io.ktor.client.*\nimport io.ktor.client.engine.cio.*\nimport io.ktor.client.plugins.logging.*\n\n// Configure a custom HttpClient\nval customHttpClient = HttpClient(CIO) {\n    install(Logging) {\n        level = LogLevel.INFO\n    }\n    // The default retry logic is not included when providing a custom client.\n    // You can add it back if needed:\n    // install(HttpRequestRetry) {\n    //     retryOnServerErrors(maxRetries = 3)\n    //     exponentialDelay()\n    // }\n}\n\n// Pass the custom client in the configuration\nval verifier = emailVerifier {\n    httpClient = customHttpClient\n}\n```\n\n### 6. Performance Considerations\n\nThe `emailVerifier {}` call performs several network requests to download the necessary data for the various checks.\nTo avoid re-downloading this data every time you want to verify an email, it is highly recommended to **create a single\ninstance of the `EmailVerifier` and reuse it throughout the lifecycle of your application**.\n\n### 7. Dynamic Data Reloading\n\nFor long-running applications, it's often necessary to refresh the data used by the verifier without restarting the application.\n`EmailVerifier` provides a set of `suspend` functions to reload the data for the checks that use external datasets.\n\nThese functions are thread-safe and will fetch the latest data from the configured `DataSource` (remote, file, or resource).\n\n```kotlin\nval verifier = emailVerifier {\n    // Your configuration...\n}\n\n// Refresh the Public Suffix List data\nverifier.updateRegistrabilityCheckerData()\n\n// Refresh the disposable email domains data\nverifier.updateDisposableCheckerData()\n\n// Refresh all data sources in parallel\nverifier.updateAllData()\n```\n\nThis is particularly useful if you want to keep your disposable email lists or other datasets up-to-date by periodically calling these methods.\n\n## 8. Logging\n\n`emailverifier-kt` uses the [SLF4J](https://www.slf4j.org/) logging facade. This allows you, as a user of the library, to choose your own logging framework (e.g., [Logback](http://logback.qos.ch/), [Log4j 2](https://logging.apache.org/log4j/2.x/), `slf4j-simple`). The library itself only includes the `slf4j-api` dependency, so it does not force a specific logging implementation on your application.\n\nBy default, no logs will be produced unless you add a logging implementation to your project's dependencies.\n\n### Enabling Logs\n\nTo see the logs from `EmailVerifier`, you need to add a dependency on an SLF4J implementation. For example, to use a simple logger that prints to standard output, you can add the following Gradle dependency:\n\n```groovy\ntestImplementation(\"org.slf4j:slf4j-simple:2.0.13\")\n```\n\n### Configuring Log Levels\n\nYou can configure the log levels for the library's loggers to control the amount of output. The main logger categories are:\n\n*   `io.github.mbalatsko.emailverifier.EmailVerifierDslBuilder`: Logs the configuration and building process of the `EmailVerifier`.\n*   `io.github.mbalatsko.emailverifier.EmailVerifier`: Logs the overall verification process for each email.\n*   `io.github.mbalatsko.emailverifier.components.checkers.*`: Loggers for individual checks (e.g., `GravatarChecker`, `SmtpChecker`).\n*   `io.github.mbalatsko.emailverifier.components.core.*`: Loggers for core components like `GoogleDoHLookupBackend` and `SocketSmtpConnection`.\n*   `io.github.mbalatsko.emailverifier.components.providers.*`: Loggers for data providers like `OnlineLFDomainsProvider`.\n\nFor example, with Logback, you could set the log level for the entire library to `DEBUG` by adding the following to your `logback.xml`:\n\n```xml\n\u003clogger name=\"io.github.mbalatsko.emailverifier\" level=\"DEBUG\"/\u003e\n```\n\n## 🔮 Roadmap\nPlanned features:\n\n* **Typo check** suggestions\n* **Multiplatform Support**\n  * Support Kotlin/Native by replacing or abstracting away java.net.IDN\n\n## ⚠️ Platform Support\n* ✅ JVM\n* ❌ Native/JS (pending IDN/punycode compatibility layer)\n\n\n## 🙋‍♂️ Contributing\nIssues, suggestions, and PRs welcome. Aim is correctness, composability, and pragmatic coverage — not full RFC simulation.\n\n#### Built for developers who want real signal, not false validation comfort.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbalatsko%2Femailverifier-kt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmbalatsko%2Femailverifier-kt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbalatsko%2Femailverifier-kt/lists"}