temp-cloak
v0.1.0
Published
Detect disposable/temporary email addresses, free webmail providers, and role accounts. Zero dependencies, works in Node and the browser, with an optional Express middleware and a memory-optimized Bloom-filter variant.
Maintainers
Readme
disposable-email-guard
Detect disposable / temporary email addresses, free webmail providers, and
role accounts (info@, support@, ...). Works in
Node and the browser, ships dual ESM + CJS + .d.ts, and includes an
optional Express middleware plus a memory-optimized Bloom-filter variant.
npm install disposable-email-guardQuick start
import { isDisposable, check, isFreeProvider, isRoleAccount } from "disposable-email-guard";
isDisposable("[email protected]"); // true
isDisposable("[email protected]"); // false
isFreeProvider("[email protected]"); // true
isRoleAccount("[email protected]"); // true
check("[email protected]");
// {
// email: "[email protected]",
// domain: "mailinator.com",
// disposable: true,
// category: "disposable",
// reason: 'domain "mailinator.com" is a known disposable provider',
// normalized: "[email protected]"
// }isDisposable (and the other helpers) accept either a full email or a bare
domain:
isDisposable("mailinator.com"); // trueAPI
isDisposable(emailOrDomain, options?) => boolean
Options:
matchSubdomains(defaulttrue) — walk parent domains, sofoo.mailinator.commatchesmailinator.com.allowlist— iterable of domains to always treat as not disposable.
isDisposable("[email protected]"); // true
isDisposable("[email protected]", { matchSubdomains: false }); // false
isDisposable("[email protected]", { allowlist: ["mailinator.com"] }); // falsecheck(emailOrDomain, options?) => CheckResult
Returns the full classification. Never throws — invalid input yields a
clean result:
check("not-an-email");
// { email: "not-an-email", domain: null, disposable: false,
// category: "clean", reason: "invalid format", normalized: null }interface CheckResult {
email: string | null;
domain: string | null;
disposable: boolean;
category: "disposable" | "free" | "role" | "clean";
reason: string;
normalized: string | null;
}isFreeProvider(emailOrDomain) => boolean
True for gmail/yahoo/outlook/hotmail/icloud/proton, etc.
isRoleAccount(email) => boolean
True when the normalized local-part is a role account (admin, info,
support, noreply, sales, contact, help, ...).
Mutating the shared list
import { addDomains, removeDomains, allowDomains } from "disposable-email-guard";
addDomains(["my-temp-mail.example"]);
removeDomains(["yopmail.com"]);
allowDomains(["partner-temp.example"]); // never flagged as disposablecreateGuard(opts?) => Guard
An isolated instance with its own domain sets — mutations don't leak into the global guard.
import { createGuard } from "disposable-email-guard";
const guard = createGuard();
guard.addDomains(["local-only.example"]);
guard.isDisposable("[email protected]"); // true
// the global isDisposable() is unaffectedYou can also seed an isolated guard with a blocklist / allowlist:
const guard = createGuard({ blocklist: ["x.com"], allowlist: ["y.com"] });
guard.isDisposable("[email protected]"); // true
guard.isDisposable("[email protected]"); // falsedatasetVersion
import { datasetVersion } from "disposable-email-guard";
datasetVersion; // { version: "0.1.0", domains: <actual count> }The domains value is the actual number of entries in the bundled list,
computed at runtime.
Express middleware
expressis an optional peer dependency. This package never imports express at runtime and types the request/response loosely, so it stays framework-agnostic.
import express from "express";
import { emailGuard } from "disposable-email-guard";
const app = express();
app.use(express.json());
app.post(
"/signup",
emailGuard({
field: "email", // string field, or (req) => string | undefined
block: ["disposable"], // categories that cause a 403
allowlist: ["partner.example"],
blocklist: ["bad.example"],
matchSubdomains: true,
attach: true, // attach result to req.emailCheck (default true)
onBlock: (req, res, next, result) => {
res.status(422).json({ message: "No throwaway emails", reason: result.reason });
},
}),
(req, res) => {
// req.emailCheck holds the full CheckResult
res.json({ ok: true, check: req.emailCheck });
},
);The middleware attaches the full result to req.emailCheck. When omitted, the
default onBlock responds with 403 JSON. Requests without an email field pass
through untouched.
Memory-optimized Bloom variant
For large deployments where memory matters, import the same API from
disposable-email-guard/bloom. It's backed by a Bloom filter: never produces
false negatives (every known disposable domain is still flagged) at the cost of
a tiny, tunable false-positive probability.
import { isDisposable } from "disposable-email-guard/bloom";
isDisposable("[email protected]"); // trueCLI
npx disposable-email-guard updateThe updater is a stub for now — fetching a remote feed is planned for a future release. Use
addDomains()/createGuard()to extend the list today.
About the data
The bundled lists are a curated STARTER SET, not exhaustive. Disposable
providers appear, disappear, and rotate domains constantly. Treat this as a
baseline signal and combine it with your own heuristics or an external feed via
addDomains(). The disposable-domain count is reported honestly through
datasetVersion.domains.
License
MIT © udayps
