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

@rassim-medkour/email-risk-triage

v0.1.0

Published

Reusable server-side email risk triage engine with deterministic checks and optional LLM analysis.

Readme

Email Risk Triage

Reusable server-side email risk triage for signup and account-quality workflows.

The package is intentionally side-effect free. It does not create users, disable accounts, delete accounts, send admin emails, or write to a database. It accepts an email address, gathers evidence, and returns a structured verdict that each host application can map to its own account lifecycle.

What It Checks

  • Email syntax and normalization.
  • Static disposable-domain blocklists.
  • DNS MX records using Node's DNS resolver.
  • Domain age through RDAP.
  • Suspicious local-part signals, such as low-vowel or digit-heavy identifiers.
  • Optional LLM classification using a provider adapter.

Verdicts

| Verdict | Meaning | | ------- | ------- | | pass | Evidence suggests a normal account. | | review | Evidence is incomplete, suspicious, or low confidence. | | reject | Deterministic evidence strongly indicates the email should not pass. |

Callers decide what those verdicts mean. For example, one platform may keep a new account disabled until pass, while another may only show review in an admin queue.

Basic Usage

import {
  EmailRiskTriageEngine,
  RdapDomainAgeProvider,
  StaticBlocklistProvider,
  createOpenAiCompatibleLlmProvider
} from "@rassim-medkour/email-risk-triage";

const engine = new EmailRiskTriageEngine({
  blocklistProvider: new StaticBlocklistProvider(["tempmail.example"]),
  domainAgeProvider: new RdapDomainAgeProvider(),
  llmProvider: createOpenAiCompatibleLlmProvider({
    apiKey: process.env.EMAIL_TRIAGE_LLM_API_KEY!,
    model: process.env.EMAIL_TRIAGE_LLM_MODEL || "gpt-4o-mini",
    baseUrl: process.env.EMAIL_TRIAGE_LLM_BASE_URL
  })
});

const result = await engine.triage("[email protected]");

Production responses (full vs redacted evidence)

By default, triage() returns full EmailRiskTriageResult.evidence: MX targets, domain age, raw SPF/DMARC strings, web-search titles/snippets, optional fetched page text, and the same structure the LLM consumes. That is ideal for debugging, admin review tools, or internal audit logs you control.

For user-facing APIs or analytics pipelines where you do not want to ship third-party page content or DNS record bodies, pass evidenceDetail: "redacted". The engine still gathers full evidence internally (and passes it to the LLM), but the returned payload strips raw TXT, parent-zone TXT rollups, and web bodies while keeping verdict, reasons, confidence, explanation, MX list, domain age, blocklist flag, and local-part signals.

const publicResult = await engine.triage(email, { evidenceDetail: "redacted" });
// publicResult.evidenceDetail === "redacted"

To redact a result you already stored (for example before sending to a client), use redactEmailRiskEvidence on result.evidence.

Kimi, OpenAI, Gemini, and Ollama Cloud

createOpenAiCompatibleLlmProvider targets OpenAI-compatible chat completion APIs with JSON schema response formatting. It can be used for OpenAI directly and for Kimi-style gateways that expose an OpenAI-compatible endpoint.

If you want Ollama Cloud’s OpenAI-compatible host (https://ollama.com/v1) instead of the native /api adapter, pass the exported constant as baseUrl:

import {
  OLLAMA_CLOUD_OPENAI_BASE_URL,
  createOpenAiCompatibleLlmProvider
} from "@rassim-medkour/email-risk-triage";

createOpenAiCompatibleLlmProvider({
  apiKey: process.env.OLLAMA_API_KEY!,
  model: "kimi-k2.5",
  baseUrl: OLLAMA_CLOUD_OPENAI_BASE_URL
});

For Ollama Cloud, use createOllamaCloudLlmProvider, which defaults to https://ollama.com/api and Authorization: Bearer with your API key. Set OLLAMA_API_KEY in the environment, or pass apiKey explicitly:

import {
  EmailRiskTriageEngine,
  NodeDnsTxtProvider,
  OllamaCloudWebPresenceProvider,
  createOllamaCloudLlmProvider
} from "@rassim-medkour/email-risk-triage";

const engine = new EmailRiskTriageEngine({
  llmProvider: createOllamaCloudLlmProvider({
    model: "kimi-k2.5"
    // apiKey: process.env.OLLAMA_API_KEY — optional if the env var is set
  }),
  dnsTxtProvider: new NodeDnsTxtProvider(),
  webPresenceProvider: new OllamaCloudWebPresenceProvider({
    fetchTopResults: 1
  })
});

Local-style model names such as kimi-k2.5:cloud are also accepted and normalized to the direct cloud model name before calling ollama.com.

Local Ollama’s OpenAI-compatible endpoint (http://localhost:11434/v1) still works via createOpenAiCompatibleLlmProvider with baseUrl and a dummy apiKey if required; only cloud access on ollama.com needs a real key.

The native Ollama Cloud provider uses Ollama's structured-output format field with the same JSON schema the generic OpenAI-compatible provider uses.

OllamaCloudWebPresenceProvider calls Ollama's web_search and web_fetch endpoints, then passes search snippets and fetched page content into the LLM evidence. This lets the model reason from actual search evidence instead of pretending it checked the internet.

Gemini should be added as a separate adapter if first-class Gemini support is needed, because its structured-output and function-calling APIs have different request shapes.

Server-Side Evidence

DNS MX, optional DNS TXT (SPF/DMARC), RDAP, and optional web-search checks run on the server. The LLM receives facts gathered by code and should not be asked to invent live DNS, WHOIS, blocklist, or web-index state.

This keeps the package:

  • Cheaper, because deterministic rejections avoid paid model calls.
  • Faster, because local checks happen before LLM classification.
  • More reliable, because network evidence is collected by code, not guessed by a model.
  • Reusable, because each host platform can provide its own blocklist, cache, logger, and enforcement policy.

Development

Requires Node 18 or newer because the package uses native fetch, AbortController, and node:dns.

npm install
npm test
npm run test:ollama # optional live Ollama Cloud smoke test
npm run typecheck
npm run build

For the live Ollama Cloud smoke test, copy .env.example to .env, set OLLAMA_API_KEY, then run:

npm run test:ollama

You can pass a different email as an argument:

npm run test:ollama -- [email protected]

By default the script prints evidenceDetail: "redacted" (no raw SPF/DMARC strings or web page bodies in JSON). The LLM still sees full evidence internally.

For a verbose dump (like older behavior), use either:

npm run test:ollama -- --full [email protected]
# or
EMAIL_TRIAGE_EVIDENCE_DETAIL=full npm run test:ollama -- [email protected]

The smoke test uses real DNS MX/TXT, RDAP, and Ollama web_search/web_fetch evidence. It can consume extra web-search and web-fetch quota.

The default new-domain threshold is 30 days. The deterministic policy flags domains with an age strictly below the threshold; a domain exactly 30 days old is eligible for LLM review or pass handling depending on the rest of the evidence.

Current Boundaries

Included:

  • Core triage engine.
  • Static blocklist provider.
  • Node DNS MX provider.
  • Node DNS TXT provider for SPF/DMARC evidence.
  • RDAP domain-age provider.
  • OpenAI-compatible structured-output adapter.
  • Ollama Cloud helper (createOllamaCloudLlmProvider, OLLAMA_API_KEY).
  • Ollama Cloud web-search evidence provider (OllamaCloudWebPresenceProvider).

Deferred to host applications:

  • Signup integration.
  • Account enable/disable behavior.
  • Admin emails.
  • Dashboards or persistence.
  • Queueing and scheduled batch jobs.