agentsclimarketplace

Block disposable emails

Skill seangeng/skills/block-disposable-emails

Claude Code skills & agent prompts I actually reach for — copy-paste, no deps. Writeups at seangeng.com.

Install
npx -y skills add seangeng/skills --skill block-disposable-emails

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Reject throwaway/disposable email addresses at signup by checking the domain against a maintained list, so spam accounts, trial-abuse, and fake signups drop sharply. Use this whenever the user is building or hardening a signup / registration / waitlist flow and mentions fake accounts, throwaway emails, mailinator / 10minutemail / temp-mail, trial abuse, bot signups, junk users, or wants to "improve signup quality" or "stop disposable emails." Reach for it when adding an auth/signup endpoint even if the user hasn't named disposable email yet — it's a cheap, high-leverage guard. Produces a server-side check (works on Node, edge runtimes like Cloudflare Workers, etc.) and the rules for layering it with a CAPTCHA and email verification.

SKILL.md

3.4 KB, as published. Nobody here has run it

Block disposable emails at signup

If anyone can sign up with any email, a chunk of your "users" will be throwaway inboxes — mailinator.com, 10minutemail.com, and hundreds more. They're how people farm free trials, dodge bans, and pad the numbers with accounts that never convert. Checking the signup domain against a community-maintained list of disposable domains takes minutes and cuts a surprising amount of junk.

The check

Use the continuously-updated disposable/disposable-email-domains list (domains.json, tens of thousands of domains). Fetch it, cache for a day, load into a Set, and membership-test the domain after the @. A Set lookup is O(1); the daily cache means you're not hammering the CDN.

const LIST =
  "https://rawcdn.githack.com/disposable/disposable-email-domains/master/domains.json";

let cache: Set<string> | null = null;
let fetchedAt = 0;

async function disposableDomains() {
  if (cache && Date.now() - fetchedAt < 86_400_000) return cache; // 1 day
  const res = await fetch(LIST);
  cache = new Set<string>(await res.json());
  fetchedAt = Date.now();
  return cache;
}

export async function isDisposableEmail(email: string) {
  const domain = email.split("@")[1]?.toLowerCase().trim();
  if (!domain) return false;
  return (await disposableDomains()).has(domain);
}

Guard the signup handler:

if (await isDisposableEmail(email)) {
  return new Response("Please use a permanent email address.", { status: 422 });
}

Rules that make it actually work

  • Server-side only. The check belongs at the signup endpoint. A client-only check is trivially skipped — never trust it.
  • Cache correctly for the runtime. On an edge runtime like Cloudflare Workers, module-scope memory may not persist between requests; back the cache with KV (or similar) so you're not refetching the list constantly.
  • Use the maintained list, not a hand-rolled regex. A regex of domains you remember goes stale the day after you write it. The list doesn't.
  • Layer it — no single signal is enough. Stack cheap independent filters: a CAPTCHA (Cloudflare Turnstile / hCaptcha) stops bots before submit, the disposable-domain block stops throwaway humans, and a verification email at the end still requires a real, owned inbox. Each is minutes of work; together they compound.

From seangeng.com/writing/block-disposable-emails. Part of github.com/seangeng/skills. Credit: @venelinkochev.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.