Skip to content

omnishield/disposable

Omnishield Disposable: is this e-mail's domain a disposable one - the free disposable-email-domains list (CC0) shipped in the package, an allow list and a deny list of the site's, subdomains included; no call while a request runs, a command to refresh the list.

Floor
Provider · Omnishield
Version
1.x
Conformance suite
runs, no CI read
Verified for real
Verified for real
Licence
MIT
composer require omnishield/disposable:1.x-dev

The disposable e-mail domains as an Omnishield list: is this e-mail's domain a throwaway one (mailinator.com, yopmail.com...)? The list is a file shipped in the package - the CC0 list of the disposable-email-domains project - read once per process; a lookup calls nothing and nothing leaves the site. A command refreshes the list from the project's repository.

Installation

composer require omnishield/disposable

PHP 8.2 or later and omnisense/omnishield: nothing else. No HTTP client while a request runs; the refresh uses the application's client when given one, PHP's own streams otherwise.

Credentials

None.

The list

data/domains.txt is disposable_email_blocklist.conf of disposable-email-domains/disposable-email-domains, unchanged, at commit 1aac72a07cd89792016dd5d2056bc418d85b8f14 (2026-10-05): 9,205 domains. Its authors dedicated it to the public domain, CC0 1.0 Universal: "You can copy, modify, distribute and use the work, even for commercial purposes, all without asking permission." data/SOURCE.md says so beside it. The project's README is candid about what a list can be: "We cannot guarantee all of these can still be considered disposable but we do basic checking so chances are they were disposable at one point in time."

The matching

The list holds second-level domains - third-level ones under a public suffix - so a domain is on it when it, or a domain above it, is: eu.mailinator.com matches mailinator.com. The e-mail's domain is lower-cased, a trailing dot dropped; single labels are never matched.

The site's lists come first: an allowed domain is never disposable, whatever the list says; a denied one always is.

Reputation
known on the deny list or the list, and not on the allow list
confidence 100 when known: a domain is on a list, not reported now and then
frequency, lastSeen 0, null
reasons [email, disposable]
data domain; matched (the part on the list) and source (deny or list); or allowed

An identity without an e-mail is unknown.

Options

Option Type Default Required
list string, a readable file null: the shipped data/domains.txt no a file of the application's: a refreshed copy; unreadable: InvalidConfigException
allow list, or one comma-separated string [] no never disposable for this site; a domain takes in its subdomains
deny list, or one comma-separated string [] no always disposable for this site

DisposableGatewayFactory takes nothing in its constructor. The gateway also has isDisposable($emailOrDomain); DisposableList (new DisposableList(?string $path)) has match($domain), count(), DisposableList::find(), parse() and refresh(), and DisposableList::SOURCE is the address it refreshes from.

Capabilities

is this identity known (ReputationInterface::lookup()) yes, by the e-mail's domain
reads email only (an address or a name are ignored)
a score confidence 100 when listed, 0 otherwise
reports no
is this token valid, is this content spam no
third party, cookies none: no call

Refreshing

vendor/bin/omnishield-disposable refresh [<file>]
vendor/bin/omnishield-disposable check <e-mail or domain>... [--list <file>]     # exit 1 when one is disposable
DisposableList::refresh('/srv/app/var/disposable_domains.txt', $httpClient);   // the number of domains written

The latest list is downloaded from the project's main branch, lower-cased, sorted, comments and blank lines dropped, and written in one move (a temporary file, renamed): a request that reads it meanwhile reads the old list or the new one, never half of either. A download of fewer than 1,000 domains is taken for broken and nothing is written. Point the gateway's list option at the file; a long-running worker reads the new list when it builds the gateway again.

Example

The shipped list, a site's allow and deny lists given as environment variables would give them (run in omnishield's workspace on 2026-10-26, offline):

<?php

require 'vendor/autoload.php';

use Omnishield\Disposable\DisposableGatewayFactory;
use Omnishield\Model\Identity;

$emails = (new DisposableGatewayFactory())->create(['allow' => 'example-school.org', 'deny' => 'throwaway.example']);
foreach (['someone@eu.mailinator.com', 'someone@gmail.com', 'pupil@example-school.org', 'x@throwaway.example', 'no-address'] as $email) {
    $reputation = $emails->lookup(new Identity(email: $email));
    printf("%-28s %s\n", $email, $reputation->known ? 'disposable: '.$reputation->data['matched'].' ('.$reputation->data['source'].')' : 'unknown '.json_encode($reputation->data));
}
someone@eu.mailinator.com    disposable: mailinator.com (list)
someone@gmail.com            unknown {"domain":"gmail.com"}
pupil@example-school.org     unknown {"domain":"example-school.org","allowed":"example-school.org"}
x@throwaway.example          disposable: throwaway.example (deny)
no-address                   unknown []

In Symfony:

omnishield:
    gateways:
        emails: { factory: disposable, options: { list: '%kernel.project_dir%/var/disposable_domains.txt', allow: '%env(default::DISPOSABLE_ALLOW)%' } }

Errors

A lookup throws nothing but a \RuntimeException when the list's file cannot be read. At create(), a list that is not readable is an InvalidConfigException. DisposableList::refresh(): an HTTP status other than 200, or a download of fewer than 1,000 domains, a ProviderException (nothing written); no answer through PHP's streams an UnreachableException (through the HTTP client: the client's UnreachableException for no answer, 5xx or 429); a file it cannot write a \RuntimeException.

Verified, and not

The family's conformance suite (Omnishield\Testing\GatewayContractTests) green on 2026-10-25, offline: Tests/DisposableContractTest.php: no request made
The shipped list on real domains done on 2026-10-07: mailinator.com, eu.yopmail.com, guerrillamail.com, 10minutemail.com disposable; gmail.com, outlook.fr, example.org, laposte.net, proton.me not
The refresh done on 2026-10-07: vendor/bin/omnishield-disposable refresh downloaded 9,205 domains with PHP's own streams, check read them
The allow and deny lists, a list of the application's by the tests

Responsibility

The list is the disposable-email-domains project's, dedicated to the public domain (CC0 1.0), copied unchanged with its source in data/SOURCE.md; this package is not affiliated with its maintainers. A domain on it "was disposable at one point in time", in the project's words: a site that refuses an address on it keeps a way for a person wrongly refused (the allow option, a contact). The MIT licence covers this package's code.

Propose a change docs/index.md