propaganistas/laravel-disposable-email
Laravel validator that rejects disposable email addresses from services like Mailinator and Guerillamail. Ships with a built-in blacklist (auto-updated weekly), a config file, the indisposable rule, and an optional command to refresh domains yourself.
Architecture fit. This package is a narrowly scoped Laravel extension, not a platform: an auto-discovered service provider that registers an indisposable validation rule through callAfterResolving, a publishable config file, an artisan command (disposable:update), a bundled domains.json fallback, and a statically cached domain set. It introduces no new runtime, datastore, queue, schema, or network service. It composes into the existing validator pipeline as a single rule string — ['email', 'indisposable'] — which means it inherits form requests, API validation, localization, test helpers, and error-bag behavior for free. From a product-management standpoint this is the ideal shape for a guardrail control: it reduces throwaway-account signups, trial farming, and fake-review seeding without committing the team to a subsystem, an owner, or a migration. It should be positioned internally as hygiene, layered alongside — not instead of — email verification, rate limiting, captcha, and any fraud scoring already in place. It is not an identity or risk-scoring product and should not be sold to stakeholders as one.
Integration feasibility. Genuinely low effort. composer require propaganistas/laravel-disposable-email, an optional vendor:publish --tag=laravel-disposable-email, and applying the rule to selected form requests is a sub-day engineering task with no design review required. Two items are real product decisions rather than mechanical toggles:
disposable:update command exists to decouple us from that cadence. Scheduling it is a mitigation, not a nice-to-have.:mx parameter. Enabling it puts a live DNS lookup on the signup validation hot path. That is a latency, availability, and false-positive tradeoff requiring an explicit fail-open/fail-closed decision and a timeout budget, signed off by whoever owns the signup funnel.Technical risk. Concentrated in six areas:
disposable:update command is the correct control; leaving it unscheduled converts a maintenance chore into a slow quality regression nobody notices until abuse metrics move.disposable/disposable blacklist. Rotating "front" domains — the documented web-library.net → mail.tm pattern — bypass a plain domain check. MX inspection narrows that gap but does not close it, and it can generate false positives where shared mail infrastructure serves legitimate senders.domains.json is exactly that, which helps).indisposable:mx trades latency and resolver availability for coverage. A slow or failing resolver can degrade signup latency or, if errors are mishandled, reject valid users.unserialize() defect was fixed in 2.1.16 — a reminder to verify serialization behavior against our cache driver rather than assume.Key questions for engineering before approval.
disposable:update daily, or accept weekly dependency bumps — and who owns that failure on-call?:mx: what timeout, are results cached per-domain, and on resolver error do we fail-open or fail-closed?disposable-email.fetcher because of proxy or egress-restricted environments, or can the scheduler reach the source list directly?Stack fit. Native fit for any Laravel 11/12/13 application on PHP 8.1+. No framework shims, no event-bus gymnastics, no config-cache caveats beyond the standard config:cache workflow. It works in Lumen (config contract injected since 2.0.12). If our validation already flows through FormRequests and Validator::make, the rule appears with zero plumbing. If parts of the platform validate outside Laravel's validator (raw JS, third-party auth providers, queued webhook ingestion), those paths need separate handling — the package cannot protect what it does not see.
Migration path. Effectively a greenfield add rather than a migration; there is no prior state to unwind. A safe path:
php artisan about / a smoke test).:mx as a separate, later decision with its own pilot, not bundled into step 2.Compatibility. Track the Laravel major support matrix explicitly — the package's own history shows majors dropped and added quickly (2.2.3 Laravel 10, 2.3.0 Laravel 11 with Laravel 8/9 and PHP 8.0 removed, 2.4.11 Laravel 12, 2.4.25 Laravel 13). Pin with a caret constraint (^2.5) and let minor/patch updates flow, because weekly patch releases are where the domain list ships. The package's own version numbers inflate quickly; do not read "2.4.x → 2.5.0" as a large change — 2.5.0 is a single feature (MX lookup).
Sequencing. Three tracks can run in parallel and should be scheduled against the release train:
disposable:update on the app's existing scheduler; confirm the host has egress to the list source or configure a custom fetcher; verify cache-driver serialization in staging.indisposable language line), the support macro for "I'm not a spammer," and the metrics we will watch post-launch.Maintenance. Low-but-nonzero, and the nonzero part is easy to forget. Ongoing obligations: (a) disposable:update scheduled — daily is the README's own recommendation and is the cheapest possible mitigation; (b) composer updates flowing so list-bearing patch releases land; (c) the dependency on the framework-upgrade checklist; (d) occasional review of false positives reported by support. Budget a small recurring allocation rather than treating this as fire-and-forget — the failure mode is silent decay, not a loud error.
Support. Support load is concentrated in one scenario: a real user with a legitimate email rejected at signup. That is a high-frustration, high-visibility ticket because it blocks the conversion funnel. Mitigations to have in place before enforcement: a clear, localized error message that names the reason; a documented exemption path (support agent can whitelist a domain or the account can be created manually); and warn-only telemetry that lets support tell "new false positive" from "known pattern." Growth and trust-and-safety teams are the two internal stakeholders who will generate the most questions; give them a dashboard rather than answering ad hoc.
Scaling. Negligible in the plain-list mode: a static, in-process set lookup. Effectively free at any realistic signup volume. The :mx mode changes the calculus — it is a per-validation DNS resolution on the signup path, so it scales with signup rate against an external resolver. At high volume this becomes a latency budget question (a slow resolver adds tail latency precisely to the flow where milliseconds matter) and a resolver-quota question. If :mx is enabled, add a short-TTL cache keyed on domain and a hard timeout well under the request timeout, and consider doing the MX check asynchronously as a post-signup flag rather than inline.
Failure modes to enumerate and pre-decide:
disposable:update plus a health check on its last-run timestamp.disposable-email.fetcher if the environment needs it; the bundled domains.json is the safety net and should be understood as stale-but-present.:mx enabled → slow or failed validation. Decision required: fail-open (accept the signup, flag for review) is almost always right for a hygiene control; fail-closed rejects real users and should be reserved for high-abuse surfaces only.Ramp-up. For the team: near zero. One engineer reads a short README, one line in a FormRequest, one scheduler entry — a half-day including tests. The real ramp-up is organizational: support needs the escalation script, growth needs to accept the conversion tradeoff, and trust-and-safety needs to know what the control does and does not catch (rotating front domains, shared MX infrastructure). Plan a short written handoff and a post-launch review at 30 days against measured false positives and observed abuse reduction. Given the low integration cost, the correct framing is "cheap, reversible hygiene control with a measurable pilot" rather than a program — and the 0-dependents/adoption signal, while a reason to keep the bundled fallback in mind, does not justify a heavier governance process than that.
How can I help you explore Laravel packages today?