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

@bsv/paymail

v2.4.10

Published

BSV Paymail library

Readme

BSV Paymail

@bsv/paymail provides a typed Paymail client and an Express router for capability discovery, public profiles, PKI, P2P transaction delivery, transaction negotiation, and simple ordinal flows.

Requirements and installation

The package supports Node.js 22 or newer. Install the package and its required SDK and Express peers:

npm install @bsv/paymail @bsv/sdk
npm install express @types/express

The router uses the host application's Express runtime and type graph and supports Express 4.18 or newer, including Express 5. Browser bundles still exclude the server router and its Express implementation.

The package publishes native ESM and CommonJS entry points with module-specific declarations. The root browser condition contains only the client, capability, and error APIs; Express router code is excluded from browser bundles.

Supported public entry points are:

  • @bsv/paymail
  • @bsv/paymail/client
  • @bsv/paymail/capability
  • @bsv/paymail/router
  • @bsv/paymail/errors

Client

import { PaymailClient } from '@bsv/paymail'

const client = new PaymailClient()
const profile = await client.getPublicProfile('[email protected]')
const capabilities = await client.getCapabilities('example.com')

console.log(profile.name, profile.avatar)
console.log(capabilities)

The default HTTP client requires HTTPS outside exact localhost development, rejects redirects and private-network destinations, pins the complete approved DNS result set for Node requests, and applies its 30-second deadline while streaming at most 1 MiB of response data. Capability documents are coalesced, cached for five minutes, and held under a 256-domain LRU ceiling per client. HttpClient exposes explicit response-size, private-network, and resolver options for controlled integration and testing; private-network access is never enabled implicitly.

The DNS pinning guarantee applies to Node, where the client controls lookup and the transport connection. Browser builds cannot replace the browser's DNS resolution or socket selection; use them only for ordinary user-initiated Paymail origins. Applications that accept attacker-selected Paymail domains or need an SSRF boundary must proxy requests through the Node client (or an equivalent server-side egress policy).

Discovery accepts only the exact bsvalias: "1.0" document shape with a bounded own-data map of string or boolean capabilities. DNS-over-HTTPS SRV answers must belong to the exact queried owner name even when DNSSEC reports the response authenticated; an authenticated but unrelated RRset is not a delegation for the requested Paymail domain.

Public-profile avatar values are restricted to credential-free public HTTPS URLs without fragments or literal/local hosts. They remain untrusted media locations: the package does not fetch or authenticate the referenced bytes. Use them only in a non-navigating image context, never as HTML/iframe/script input, and use a DNS-pinned media proxy with MIME and size enforcement when the application needs stronger content isolation.

Server router

Domain handlers receive the Express route-parameter object followed by the validated request body. Use getNameAndDomain rather than parsing the handle again:

import express from 'express'
import { PaymailRouter, PublicKeyInfrastructureRoute, PublicProfileRoute } from '@bsv/paymail'

const app = express()

const profileRoute = new PublicProfileRoute({
  domainLogicHandler: async params => {
    const { name, domain } = PublicProfileRoute.getNameAndDomain(params)
    const user = await fetchUser(name, domain)
    return {
      name: user.alias,
      avatar: user.avatarUrl
    }
  }
})

const pkiRoute = new PublicKeyInfrastructureRoute({
  domainLogicHandler: async params => {
    const { name, domain } = PublicKeyInfrastructureRoute.getNameAndDomain(params)
    const user = await fetchUser(name, domain)
    return {
      bsvalias: '1.0',
      handle: `${name}@${domain}`,
      pubkey: user.identityKey
    }
  }
})

const paymail = new PaymailRouter({
  baseUrl: 'https://paymail.example.com',
  routes: [profileRoute, pkiRoute]
})

app.use(paymail.getRouter())
app.listen(3000)

baseUrl is the externally reachable origin advertised in the /.well-known/bsvalias capability document. basePath can be supplied when the router is mounted below the origin root. Production origins must use HTTPS; plain HTTP is accepted only for exact localhost development. The router validates every Paymail handle before domain logic, returns 400 for malformed JSON, and rejects conflicting sender-validation configuration. The router snapshots its validated origin, path, route descriptors, capability codes, handlers, and sender-validation mode during construction. Mutating the legacy public configuration fields or caller-owned route arrays afterwards does not rewrite the mounted routes or discovery authority. Capability codes must be unique, and capability metadata is copied before a derived BFRC is computed.

Payment-destination handlers must return canonical hexadecimal scripts and non-negative integer outputs whose total exactly equals the requested amount. Ordinal-destination handlers must return exactly the requested number of scripts. The router snapshots request amounts, transaction encodings, and identity parameters before invoking application code, so handler mutation cannot move a response check away from the wire request. Transaction-receive handlers must return the txid of the raw or BEEF transaction passed to them; an unrelated acknowledgement is treated as an internal handler failure rather than sent to the client.

The router cannot determine an application's reference-token semantics or crediting policy. Before broadcasting, crediting, or acknowledging a transaction, domain logic must validate that its outputs satisfy the exact recipient and reference, apply application-specific transaction policy, and claim the txid/outpoints idempotently in durable state.

Transaction-negotiation requests are public, untrusted protocol input. The route validates their structural fields and transaction framing, not sender identity, thread authorization, freshness, supported embedded protocols, Merkle/miner evidence, or callback-token ownership. Domain logic must validate those policies before storing, notifying, signing, broadcasting, or using a reply_to destination; a public-looking HTTPS peer-channel URL is not an authentication verdict.

Cross-origin deployment

The library does not impose CORS or CSP policy. Paymail capability endpoints are public protocol surfaces and commonly need to remain callable by deployed applications across unrelated domains, webviews, and future clients. Configure CORS at the host application or edge:

  • keep credential-free public APIs broadly accessible by default when the service contract requires it;
  • make exact-origin allowlists and credentialed origin handling explicit operator choices;
  • never combine wildcard origins with credentialed requests; and
  • treat CSP as a document/UI policy, not as API authorization.

Authentication and authorization must be enforced by the protocol or route logic rather than by assuming browser-origin headers are an access-control boundary.

Signing compatibility

createP2PSignature produces the compact Base64 Bitcoin Signed Message form accepted by the raw, BEEF, and ordinal receive routes when signature verification is enabled. verifySignature defaults to false for legacy compatibility; in that mode metadata is untrusted and must never authorize a sender. The receiver verifies the transaction-ID signature locally before performing the Paymail ownership lookup for the declared public key, so malformed signatures cannot trigger outbound discovery work. The router historically derives its requestSenderValidation advertisement from these receive routes; an explicit value must agree with them.

That historical advertisement reuses BRFC 6745385c3fc0, which the upstream Paymail specification defines for signed, timestamped Basic Address Resolution requests—not for P2P transaction metadata. Treat it only as this package's legacy statement about its configured receive routes. It does not prove standards-compliant payer validation, timestamp checking, or replay defense. Correcting the meaning requires a coordinated capability-document migration; silently removing or reinterpreting the deployed code would break wire discovery behavior.

The legacy Paymail signature preimage contains only the transaction ID. It does not cryptographically bind the route recipient, reference, sender handle, or endpoint authority. Treat it only as proof that the resolved key signed that transaction ID, and independently enforce recipient/output/reference and replay checks in domain logic. Full contextual sender authorization requires a new versioned signature preimage and coordinated protocol migration; silently changing the existing preimage would break deployed signatures.

Development and verification

From the repository root:

pnpm --filter @bsv/paymail format:check
pnpm --filter @bsv/paymail lint
pnpm --filter @bsv/paymail typecheck
pnpm --filter @bsv/paymail test:coverage
pnpm --filter @bsv/paymail pack:check
pnpm --filter @bsv/paymail test:browser
pnpm --filter example-paymail test

Tests are deterministic and must not depend on public Paymail or DNS services. The private examples under docs/examples are compiled fixtures for explicit manual use; their external-service credentials and example private keys are supplied through environment variables and must never be committed.

pack:check installs the exact dry-packed tarball into clean ESM and CommonJS consumers and validates declarations, export maps, publint, and payload hygiene. test:browser installs that same tarball and measures Vite and esbuild bundles against the checked-in browser budget.

Package publishing is performed only by the repository release workflow. Do not publish from a developer workstation.

Additional API and protocol material is available in docs. Please report defects through the ts-stack issue tracker.

License

Current TS Stack changes are licensed under the Open BSV License Version 6; see LICENSE.txt. This package also retains pre-uniformization code under its package-specific Open BSV License Version 4 grant. Redistributors must preserve THIRD_PARTY_NOTICES.md and the applicable text in LICENSES/.