@quarg/burnerguard
v1.0.0-rc1
Published
Detect disposable and burner email addresses. Static blocklist mode with zero dependencies, or connect to the BurnerGuard API for real-time risk scoring.
Maintainers
Readme
BurnerGuard
Stop disposable and burner email addresses from polluting your signup flow. BurnerGuard ships a curated blocklist with zero runtime dependencies, works in any JavaScript environment, and gives you a clean async API to verify emails against it.
Down the road, adding an API key will upgrade the same verify() call to real-time risk scoring from the BurnerGuard service — but the static library is fully functional on its own.
Part of the Quarg developer tools platform.
Why this library?
Disposable email detection sounds simple — just check a domain against a list. In practice, you end up solving the same problems every time:
- Raw domain lists leave you to handle parsing, deduplication, lookup optimization, email validation, and keeping the list current. Every project rebuilds this from scratch.
- Minimal wrappers give you a function but stop there — no allowlists to handle false positives, no batch operations, no way to bring your own data.
- Full email verification platforms pull in DNS and SMTP dependencies, add latency, and introduce complexity you may not need when all you want is a fast domain check.
BurnerGuard fills the gap: a fast, flexible, self-updating email checker that works offline, supports allowlists, handles batch operations, and ships with zero runtime dependencies.
Honest limitations
A static blocklist catches known disposable domains, but it can't detect brand-new throwaway domains, catch-all configurations, or suspicious registration patterns. If you need real-time detection with domain age analysis, MX provider fingerprinting, velocity signals, and risk scoring, the BurnerGuard service is on the way. The upgrade path will be seamless — add an API key and the same verify() call returns enriched results.
Features
- Fast lookups — Domains are stored in a
Setfor constant-time hash lookups. Subdomains are resolved by walking up the domain hierarchy, so even deep subdomains likea.b.c.throwaway.exampleresolve quickly. - Subdomain detection — If
throwaway.exampleis blocked,mail.throwaway.exampleis automatically caught too. Works at any depth. - Allowlist support — Explicitly permit domains that would otherwise be matched. The allowlist takes priority at every level of the domain hierarchy, so you can block
example.netbut allowlegit.example.net. - Flexible input — Pass a full email address or a bare domain to any method. BurnerGuard detects which one you gave it.
- Batch operations — Verify a list of emails, filter them into matched and clean buckets, or check whether any email in a set matches.
- Fully async — Every verification method returns a
Promise, so the API won't change when service mode adds network calls. In static mode, the underlying work is synchronous — the async wrapper adds negligible overhead. - Bring your own data — Use the bundled blocklist out of the box, load from a local file, fetch from a URL, pass an inline array, or combine multiple sources. When custom sources are provided, the bundled list steps aside unless you explicitly keep it.
- Stays current — The bundled blocklist is updated automatically and published as new patch versions. Just run
npm updateto get the latest domains. - Lazy-loaded and memory-conscious — The bundled blocklist is loaded via dynamic
import()only whencreate()is called, not at module import time. If you're using custom sources exclusively, the bundled data is never loaded at all. - Universal — No Node.js built-ins in the core path. Works in Node.js, Deno, Bun, Angular, React, Vite, edge runtimes, and serverless functions. File-based sources dynamically import
node:fsonly when you opt into them. - Zero runtime dependencies — Nothing beyond platform built-ins.
- TypeScript-first — Full type definitions with a private-constructor factory pattern that guarantees every instance is fully initialized before you can use it.
Requirements
Node.js 20 or later. Works in Deno, Bun, and edge runtimes with no additional configuration.
Installation
npm install @quarg/burnerguardyarn add @quarg/burnerguardpnpm add @quarg/burnerguardQuick start
import {BurnerGuard} from '@quarg/burnerguard';
const guard = await BurnerGuard.create();
const result = await guard.verify('[email protected]');
// {
// isMatch: true,
// domain: 'throwaway.example',
// matchedOn: 'blocklist',
// isAllowlisted: false
// }Usage
Verifying a single email or domain
verify() accepts a full email address or a bare domain. It returns a VerifyResult with everything you need to make a decision.
await guard.verify('[email protected]'); // email address
await guard.verify('throwaway.example'); // bare domain — works too
await guard.verify('mail.throwaway.example'); // subdomain — caught automatically
await guard.verify('[email protected]'); // clean — { isMatch: false, ... }Verifying a batch
verifyBatch() checks multiple inputs and returns a result for each, in the same order.
const results = await guard.verifyBatch([
'[email protected]',
'[email protected]',
'[email protected]'
]);
// [{ isMatch: false, ... }, { isMatch: true, ... }, { isMatch: false, ... }]Filtering a list
filter() splits your list into two buckets: matched (disposable) and clean.
const {matched, clean} = await guard.filter([
'[email protected]',
'[email protected]',
'[email protected]'
]);
// matched: ['[email protected]']
// clean: ['[email protected]', '[email protected]']Checking if any email matches
hasMatch() short-circuits on the first match — useful when you just need a yes/no answer for a batch.
await guard.hasMatch(['[email protected]', '[email protected]']); // true
await guard.hasMatch(['[email protected]', '[email protected]']); // falseBlocking and allowing domains at runtime
You can add domains to the blocklist or allowlist after initialization. These changes apply to the current instance only.
await guard.block('sketchy.example');
(await guard.verify('[email protected]')).isMatch; // true
await guard.allow('false-positive.example');
(await guard.verify('[email protected]')).isMatch; // falseAdding extra domains at creation
If you know upfront which domains to add, pass them as options. These are merged with the bundled blocklist.
const guard = await BurnerGuard.create({
additionalBlockedDomains: ['sketchy.example'],
additionalAllowedDomains: ['legit-but-flagged.example']
});Loading from custom sources
You can load domain lists from local files, remote URLs, or inline arrays. When custom sources are provided, the bundled blocklist is not loaded by default — pass useBundledBlocklist: true to keep it.
// From local files (Node.js only)
const guard = await BurnerGuard.create({
sources: [
{type: 'block', filePath: '/path/to/my-blocklist.txt'},
{type: 'allow', filePath: '/path/to/my-allowlist.txt'}
]
});
// From a URL (works everywhere — uses native fetch)
const guard = await BurnerGuard.create({
sources: [{
type: 'block',
url: 'https://example.com/my-blocklist.txt'
}]
});
// From an inline array
const guard = await BurnerGuard.create({
sources: [{type: 'block', list: ['spam.example', 'junk.example']}]
});
// Mix and match — bundled list + custom sources
const guard = await BurnerGuard.create({
useBundledBlocklist: true,
sources: [{type: 'block', list: ['my-extra-domain.example']}]
});Service mode (coming soon)
Service mode is not yet available. When it launches, you'll add an API key and the same verify() call will return enriched results with risk scoring. No code changes required.
const guard = await BurnerGuard.create({
apiKey: 'bg_live_...',
threshold: 0.7
});
const result = await guard.verify('[email protected]');
// {
// isMatch: true,
// domain: 'sketchy.example',
// matchedOn: 'domainAge',
// isAllowlisted: false,
// verdict: 'block',
// riskScore: 0.94,
// signals: {
// blocklist: false,
// domainAgeDays: 4,
// mxProvider: 'catch-all',
// catchAll: true,
// roleAddress: false,
// patternCluster: false,
// velocity: false
// }
// }API Reference
BurnerGuard.create(options?)
Creates and initializes a new instance. Returns Promise<BurnerGuard>.
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| apiKey | string | — | BurnerGuard API key. Enables service mode (coming soon). |
| threshold | number | 0.5 | Risk score threshold (0.0–1.0). Service mode only. |
| sources | DomainListSource[] | — | Domain list sources: file path, URL, or inline array. |
| additionalBlockedDomains | string[] | — | Extra domains to add to the blocklist. |
| additionalAllowedDomains | string[] | — | Extra domains to add to the allowlist. |
| useBundledBlocklist | boolean | true* | Whether to load the bundled blocklist. *Defaults to false when custom sources are provided. |
| useBundledAllowlist | boolean | false | Whether to load the bundled allowlist. |
Async methods
| Method | Returns | Description |
|--------|---------|-------------|
| verify(emailOrDomain, options?) | Promise<VerifyResult> | Verify a single email or domain. |
| verifyBatch(emailsOrDomains, options?) | Promise<VerifyResult[]> | Verify multiple inputs. Returns results in the same order. |
| filter(emailsOrDomains, options?) | Promise<FilterResult> | Split inputs into {matched, clean}. |
| hasMatch(emailsOrDomains, options?) | Promise<boolean> | Returns true if any input matches. Short-circuits on first match. |
| block(domain) | Promise<void> | Add a domain to the blocklist at runtime. |
| allow(domain) | Promise<void> | Add a domain to the allowlist at runtime. |
Sync methods
| Method | Returns | Description |
|--------|---------|-------------|
| extractDomain(email) | string \| null | Extract the domain portion from an email address. |
| isValidEmail(email) | boolean | Check whether a string is a valid email address (syntax only). |
Properties
| Property | Type | Description |
|----------|------|-------------|
| blocklistSize | number | Number of domains currently in the blocklist. |
| allowlistSize | number | Number of domains currently in the allowlist. |
VerifyResult
Returned by verify() in static mode.
{
isMatch: boolean; // true if the domain matched the blocklist
domain: string | null; // the extracted domain, or null for invalid input
matchedOn: string | null; // the signal that triggered the match (e.g., "blocklist"), or null
isAllowlisted: boolean; // true if the allowlist overrode a potential match
}EnrichedVerifyResult
Returned by verify() in service mode (coming soon). Extends VerifyResult with additional fields.
{
// ... all VerifyResult fields, plus:
verdict: 'allow' | 'block' | 'suspect';
riskScore: number; // 0.0 (safe) to 1.0 (certain threat)
signals: RiskSignals; // detailed signal breakdown
}FilterResult
Returned by filter().
{
matched: string[]; // inputs that matched the blocklist
clean: string[]; // inputs that did not match (or were allowlisted)
}Bundled data and updates
BurnerGuard ships with a pre-processed copy of the disposable-email-domains community blocklist and allowlist. It works offline out of the box — no network calls needed.
The bundled data is updated automatically and published as new patch versions. Just run npm update to pick up new domains. No manual list management required.
To update the bundled data manually:
npm run update-blocklist
npm run buildAcknowledgements
- Domain data sourced from the disposable-email-domains community blocklist.
- Email validation regex derived from Angular's form validation.
License
MIT — see LICENSE.
