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

@flashyos/mail

v0.3.0

Published

mail/1 and the estate's mail control plane: a lane policy that refuses, a capture that demands a consent basis, a content-free event record, and a transport seam so the provider is a variable.

Readme

@flashyos/mail

The estate's mail control plane, and mail/1, the record underneath it.

Not a mail provider. The provider is a constructor argument.

import { createMailGateway, resendTransport, capture } from '@flashyos/mail';

const gateway = createMailGateway({
  from: 'FlashyOS <[email protected]>',
  org: 'org/flashyos',
  transport: resendTransport({ apiKey: process.env.RESEND_API_KEY! }),
  recorder: { append: (leaf) => db.mailLeaf.create({ data: leaf }) },
  suppression: { suppressed: (a) => db.suppression.exists(a) },
});

await gateway.send(
  { to: user.email, subject: 'Reset your password', text: link },
  { lane: 'transactional', from: 'org/flashyos', author: 'agent/flashyos-ci' },
);

The package takes configuration and never reads your environment or imports your error class. That boundary is what makes it a package rather than a copy with extra steps — the same boundary @flashyos/llm-gateway draws, for the same reason.

What it is for

The estate carries 11 independent mail integration files across 7 repositories, all pointed at one provider, and no two of them agree about anything: one throws on failure and one never throws, one has a suppression list and six do not, one has an unsubscribe and six do not. None of them records that a message existed. So a domain that silently stopped delivering looks exactly like a week when nobody wrote to anyone — and on 6 September 2026 three estate domains were sending unauthenticated into their own published DMARC policy, which nothing anywhere was red about. docs/mail.md in this repository has the measurement and the decision.

The five things it does

It refuses. lanes.ts is the policy and every rule in it is a refusal. An agent may not send to a person who did not just ask us for something; it may draft, and a named human sends. That is estate doctrine — agents suggest, humans consent — arriving on the one channel nobody had gated.

It demands a basis. capture() will not turn an address into a usable record without an https URL showing what the person was told. An address with no basis is not an asset; it is a liability with a mailing list attached.

It records. Every outcome — sent, held, refused, failed — produces a mail/1 leaf. A refusal that leaves no trace is indistinguishable from a message nobody tried to send.

It folds in what the provider says afterwards. A delivery outcome is a transition carrying supersedes, never an edit — so sent does not stay sent forever, and the current state of a message is derived from its transitions the way holding/1 derives a position's. Bounces and complaints feed the suppression list, which every lane consults except transactional.

It keeps the provider replaceable. resendTransport, logTransport, failover(...). Swapping providers is a constructor argument, which is what makes "are we locked in" a question with a measurable answer.

The two contracts it inherits, and the one it adds

Both inherited contracts come from packages/api/src/lib/email.ts, which had them right:

  1. Never throw on delivery. Mail is always attached to something more important than itself. A provider outage must not become a 500 on the settlement that triggered it.
  2. Fall back to logging. With no transport configured the flow works end to end, so self-hosting and local development need no account anywhere.

And the one that is new, and is the reason the package exists:

  1. Always record.

A refusal is the one thing that throws, because a refusal is a bug in the caller rather than weather.

Reading the record

mail/1 is content-free by construction: the counterparty's domain, never the address; no subject, body or message id; and an address is never hashed into a leaf, because a hashed small-space identifier is a reversible identifier. The full rules and the reasoning are in SPEC.md.

Travelling

The format travels by file, the way every record format in this estate does. vendor-mail.mjs is dependency-free — node: builtins only — and runs in a repository with no package.json and no node_modules:

node vendor-mail.mjs emit              # mail.leaves.json  →  mail.fragment.json
node vendor-mail.mjs validate          # what is wrong with a projection
node vendor-mail.mjs check flashy.gold # can this domain authenticate its mail?

tools/adoptable.test.mjs copies it alone into an empty directory, runs it with bare node and hands the output to this package's own validator. tools/vendored-mail.test.mjs runs both implementations over one corpus and fails on any disagreement about a verdict — never by comparing constants, which is the check that would pass on the day one copy stopped using the rule.

Status

Published, at 0.2.0. The excuse for keeping it private said the gateway had no caller in production; packages/api is one, and a format the platform itself runs on is past the bar the estate's other unpublished packages are waiting at.

Domain ownership is refused from 0.2.0 and a receipt footer is appended from 0.3.0DOMAIN_OWNERSHIP_SINCE and RECEIPT_FOOTER_SINCE are exported so both figures are read from code rather than remembered, and tools/behaviour-markers.mjs holds these sentences and those constants to each other. A minor bump on a 0.x package crosses the caret boundary, so a consumer pinned at ^0.1.0 must move: that is the cost of the behaviour changing, paid once, rather than two programs under one version.

The receipt footer

One line at the foot of every message the gateway sends, pointing at a public record of who sent it. Opt-in: with no receipt in the config there is no footer, because appending a link to a page an adopter never published would put a 404 at the foot of their mail.

const mail = createMailGateway({
  from: 'Flashy Gold <[email protected]>',
  org: 'org/flashy-gold',
  transport, recorder,
  receipt: { base: 'https://flashyos.com/receipt', org: 'org/flashy-gold' },
})

The link carries the organisation and the lane, and nothing else. Not the message — mail/1 §6a refuses a per-message row in a public document, and a footer link is the worst place to put something that has to stay unguessable: it ends up in spam corpora, forwarded threads and support tickets. The lane is a class of message the recipient's own inbox already tells them, so it adds no fact anybody did not have, and it lets the page answer what permits an organisation to send you this kind of thing.

Two refusals. It never contains the word "unsubscribe" — the gateway decides whether a bulk message carries a working unsubscribe by looking for that word beside a URL in the body, and a footer on every message that used it would satisfy that check for a campaign carrying none. transactional gets no footer: a password reset is caused by something the reader did seconds ago.

Proving the domain is yours

check() proves a domain authenticates mail. It does not prove anybody owns it — reading somebody's public DNS says nothing about who put the records there, so a tenant can name a domain they have nothing to do with and check() will call it healthy. That was tolerable while every organisation on a platform belonged to one estate. It stops being tolerable the moment a stranger can create one.

import { challenge, recordFor, proveOwnership } from '@flashyos/mail'

const c = challenge('flashy.gold', nonceIssuedByThePlatform)
recordFor(c)   // → { host: '_flashyos-challenge.flashy.gold', value: 'flashyos-domain-verification=…' }

const reading = await proveOwnership(c, resolver)
// → { verdict: 'proven' | 'absent' | 'expired' | 'unasked', host, value, expiresAt }

Or, with no toolchain at all, from the machine that can see your own zone:

node vendor-mail.mjs prove flashy.gold <the nonce you were given>

Neither this package nor the vendored file will mint you a nonce. A nonce no platform recorded proves nothing to anybody, and a command that appeared to work and settled nothing is worse than no command. challenge() refuses anything under 32 hex characters for the same reason one step earlier: a guessable challenge is a challenge anybody can satisfy.

Four verdicts, and unasked is again the one that matters — a resolver that did not answer has not told you the record is missing, and recording that as a failure would let one flaky lookup revoke a proof sitting correctly in a zone.

The compensating check

A record committed to a repository says nothing about a zone. check() reads SPF, DKIM and DMARC over DNS the way a receiving mail server would, and it is the only question in this package a checkout cannot answer.

import { check, alignsVia } from '@flashyos/mail'
import { Resolver } from 'node:dns/promises'

const reading = await check('flashy.gold', new Resolver({ timeout: 4000 }))
// → { verdict: 'refused-by-own-policy', spf: null, dkim: null, dmarc: 'reject', why: … }
alignsVia(reading) // → 'none'

Or, with no toolchain at all:

node vendor-mail.mjs check flashy.gold

Four verdicts, and the fourth is the one that matters. unasked means the resolver did not answer — reported as itself, never as absent, because reporting the asking machine's silence as a fact about somebody's domain is how a report invents a defect.

alignsVia folds a reading into the three states a sender record stores, and spf-only is deliberately its own answer rather than part of ok: SPF authorises a set of hosts, and whether that set contains the provider actually sending cannot be read without resolving every nested include and knowing that provider's current egress. A DKIM key on the apex is provable from outside.