npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sdxc/email-auth

v2026.11.3

Published

Parse and evaluate a domain's email-authentication DNS policies: DMARC, SPF, MTA-STS, TLS-RPT, BIMI and DKIM

Readme

@sdxc/email-auth

Parse and evaluate a domain's email-authentication DNS policies: DMARC, SPF, MTA-STS, TLS-RPT, BIMI and DKIM.

Every parser is pure and returns a Result; every lookup goes through DNS over HTTPS, so the same checks run in a Cloudflare Worker, a server or a test. DMARC follows RFC 9989, with the np, psd and t tags and the DNS Tree Walk in place of the public suffix list.

Installation

npm add @sdxc/email-auth

Results come from @sdxc/result and lookups from @sdxc/doh; both install alongside it.

Usage

Discover a domain's DMARC policy

import { discoverDmarc } from "@sdxc/email-auth";
import { isSuccess } from "@sdxc/result";

let discovery = await discoverDmarc("news.example.com");
if (isSuccess(discovery)) {
	discovery.data.policy; // "quarantine": the record's sp=, since news.example.com has none
	discovery.data.policyDomain; // "example.com"
	discovery.data.organizationalDomain; // "example.com"
	discovery.data.record?.testing; // t=y
}

policy is null when DMARC does not apply. A resolver failure is the Failure, never "no policy".

Count SPF lookups, or evaluate a sender

import { analyzeSpf, checkHost } from "@sdxc/email-auth";

let audit = await analyzeSpf("example.com");
// audit.data.lookups: DNS-querying terms across every include; over 10 is a permerror
// audit.data.problems: [{ code: "too-many-lookups", lookups: 13 }, …]

let evaluation = await checkHost({
	ip: "192.0.2.10",
	domain: "example.com",
	sender: "[email protected]",
});
evaluation.result; // "pass" | "fail" | "softfail" | "neutral" | "none" | "temperror" | "permerror"
evaluation.mechanism; // "mx"

Read MTA-STS and TLS-RPT

import { discoverMtaSts, discoverTlsRpt, fetchMtaStsPolicy } from "@sdxc/email-auth";

let record = await discoverMtaSts("example.com"); // { status: "found", record: { id } }
let policy = await fetchMtaStsPolicy("example.com"); // { mode: "enforce", mx: […], maxAge }
let reporting = await discoverTlsRpt("example.com"); // { status: "found", record: { rua } }

The policy file is fetched like a sending MTA does: HTTPS, no redirects, a 200 text/plain body under 64 KiB, through @sdxc/outbound.

Check whether a domain qualifies for BIMI

import { bimiDmarcShortfalls, discoverBimi, discoverDmarc } from "@sdxc/email-auth";

let bimi = await discoverBimi("example.com"); // { status: "found", record: { location, evidence } }
let dmarc = await discoverDmarc("example.com");
// bimiDmarcShortfalls(dmarc.data): [] when DMARC enforces, else ["policy-none"], ["testing"], …

BIMI follows draft-brand-indicators-for-message-identification-14. The logo URL is validated as HTTPS and SVG, and never fetched.

Parse records and reports you already have

import {
	parseDkimRecord,
	parseDmarcRecord,
	parseDmarcReport,
	parseSpfRecord,
} from "@sdxc/email-auth";

parseDmarcRecord("v=DMARC1; p=reject; rua=mailto:[email protected]");
parseSpfRecord("v=spf1 mx include:_spf.example.net -all");
parseDkimRecord("v=DKIM1; k=rsa; p=MIGfMA0GCSq…");
parseDmarcReport(xml); // an RFC 9990 aggregate report, already decompressed

Audit a whole domain

import { checkEmailAuth } from "@sdxc/email-auth";

let report = await checkEmailAuth("example.com", { dkimSelectors: ["google"] });
report.ok; // false when any policy is missing, invalid or unreadable
report.results; // [{ policy: "dmarc", verdict: "pass", summary: "p=reject", problems: [] }, …]

Command line

bunx @sdxc/email-auth check example.com

With @sdxc/kit installed, sdxc pkg email-auth … runs the same command line, using the version your project installs.

check grades DMARC, SPF, MTA-STS (with its policy file and MX coverage), TLS-RPT, BIMI and DKIM for each --selector. It exits 0 when every policy holds, 1 when one is missing, invalid or unreadable, and 2 on a usage error.

sdxc-email-auth check example.com --selector google --selector s1
sdxc-email-auth check example.com --skip bimi,tls-rpt --json

sdxc-email-auth check --help lists every flag.

API

DMARC

  • parseDmarcRecord(text): a record, or a failure when v=DMARC1 is not first. Invalid values fall back to their defaults and are listed in issues, as receivers do.
  • appliedDmarcPolicy(record, scope?): the policy for the domain, an existing subdomain or a missing one, with np → sp → p fallback and none for a reporting-only record.
  • discoverDmarc(domain, options?): policy discovery: the record, where it was published, and the policy a receiver applies.
  • dmarcTreeWalk(domain), walkDmarcTree(domain, options?): the names a Tree Walk queries, and the walk itself.
  • selectOrganizationalDomain(domain, found), findOrganizationalDomain(domain, options?): the Organizational Domain from a walk's records, or by running one.
  • isAligned(authorDomain, identifier, mode, options?): strict or relaxed identifier alignment.
  • isDmarcRecord(text): whether a TXT record starts with v=DMARC1.
  • parseDmarcReport(xml): an aggregate report, RFC 9990 or RFC 7489 shaped.

SPF

  • parseSpfRecord(text), isSpfRecord(text): the record's mechanisms and modifiers.
  • checkHost(input, options?): RFC 7208 check_host(), with the 10-lookup limit, the void-lookup limit (maxVoidLookups, default 2), macros and exp explanations.
  • analyzeSpf(domain, options?): the record, its lookup count across includes and redirects, and its problems.

BIMI

  • parseBimiRecord(text), isBimiRecord(text): the logo (l=), evidence (a=), local-part selectors (lps=) and avatar preference (avp=); declined for an explicit l=; a=;.
  • discoverBimi(domain, options?): the record at <selector>._bimi.<domain>, then at the Organizational Domain; pass organizationalDomain to skip the Tree Walk.
  • bimiDmarcShortfalls(discovery): why a DMARC discovery falls short of BIMI's requirements.

MTA-STS, TLS-RPT and DKIM

  • parseMtaStsRecord(text), parseMtaStsPolicy(text), discoverMtaSts(domain, options?), fetchMtaStsPolicy(domain, options?), mtaStsPolicyUrl(domain), matchesMxPattern(host, patterns).
  • parseTlsRptRecord(text), discoverTlsRpt(domain, options?).
  • parseDkimRecord(text), lookupDkimKey(domain, selector, options?): DKIM1 key records (RFC 6376). DKIM2 (draft-ietf-dkim-dkim2-spec) is being tracked and not parsed yet.
  • checkEmailAuth(domain, options?): every policy above graded pass, warn, fail or error, with dkimSelectors, bimiSelector and skip; EMAIL_AUTH_POLICIES lists them.
  • RecordParseError: every parser's failure, with the protocol it was read as. PolicyFetchError: a policy file fetch that failed, with the step in reason.

Every lookup takes { resolver, signal, timeoutMs }, passed to @sdxc/doh.

Versioning

Releases are dated rather than semantic. A version is the UTC date it was published, written YYYY.M.D, so 2026.9.4 is the release from 4 September 2026. At most one release goes out per day.

Those numbers say when, not what: a later date means a later release and carries no compatibility promise. Any release may change or remove an export.

Depend on one exact date, and move it when you are ready to take the change:

{
	"dependencies": {
		"@sdxc/email-auth": "2026.9.4"
	}
}

A caret or tilde range reads the date as major, minor and patch, so it accepts every later release in the same year. An exact version keeps the upgrade yours to schedule.

License

MIT

Author

Sergio Xalambrí