Files
2026-06-09 21:06:27 +01:00

22 KiB

@suaveplan/hibp - Privacy-preserving Have I Been Pwned v3 client — k-anonymity range API, authenticated breach/paste API, LRU cache, differential-privacy mode

Summary

Privacy-preserving Have I Been Pwned v3 client. Universal — works in Node, Bun, Deno, browsers, edge workers. The only required runtime primitive is fetch.

Documentation tree

  • breach-client — BreachClient wraps the four authenticated HIBP v3 endpoints: /breachedaccount/{email}, /breach/{name}, `/breaches…
  • rate-limit-gate — RateLimitGate enforces the HIBP v3 client-side rate-limit contract of one request per 1500 ms across the authenticate…
  • retry-after — parseRetryAfter parses the Retry-After HTTP header per RFC 9110 §10.2.3 and returns the indicated delay in millisec…
  • cache — (no submodules) - lru — LruHibpCache<V> is the default cache for `H…
    • cache — (no submodules) - lru — LruHibpCache<V> is the default cache for `H…
    • lru — LruHibpCache<V> is the default cache for HibpClient. It holds up to LRU_DEFAULT_CAPACITY entries (10 000) with `L…
    • null — NullHibpCache<V> is the always-miss adapter used by HibpClient when the caller explicitly opts out of caching (`cac…
  • client — (no submodules) - options.schema — HibpClientOptionsSchema is the Z…
    • client — (no submodules) - options.schema — HibpClientOptionsSchema is the Z…
    • options.schema — HibpClientOptionsSchema is the Zod boundary for the HibpClient constructor. It is .strict() — unknown keys cause…
  • codes — Spec-canonical path for the HIBP error-code registry. This file is a thin re-export of the top-level `src/error-codes.t…
  • hibp-error — HibpError is the structured error class every failure path inside @suaveplan/hibp throws. It carries one of the sev…
  • observability — (no submodules)
  • privacy — (no submodules)
  • range — (no submodules)
    • constant-time-compare — constantTimeEqualsHex compares two hex suffixes without early-exit on the first mismatching character. It is used by…
    • differential-privacy — padPrefixes generates the decoy prefixes used by the differential-privacy mode of checkPassword. Given the real 5-h…
    • range-mode — Houses the hash-mode dispatch utilities used by checkPassword: hashSha1Prefix (SHA-1 via @noble/hashes/sha1), `ha…
    • range-stream-parse — Streams an HTTP response body line-by-line through a TextDecoderStream and a newline framer, yielding `{ suffix, coun…
    • range-strict-frame — parseRangeLine is the per-line framer used by streamParseRangeBody. It enforces the documented Pwned Passwords line…
    • range — (no submodules)
  • breach.schema — BreachSchema is the Zod schema mirroring the HIBP v3 Breach payload. It validates every documented field, so the re…
  • paste.schema — PasteSchema validates the HIBP v3 Paste payload returned by /pasteaccount/{email}. Older paste records can omit `…
  • pwned-result.schema — PwnedResultSchema validates the runtime shape of every checkPassword result. It is the final boundary between the i…
  • error-codes — Hosts the canonical HIBP_CODES registry — exactly seven error codes that classify every failure path inside `@suavepl…
- lru — `LruHibpCache` is the default cache for `H…
  • cache — (no submodules)- lru — LruHibpCache<V> is the default cache for `H…

  • lru — LruHibpCache<V> is the default cache for HibpClient. It holds up to LRU_DEFAULT_CAPACITY entries (10 000) with `L…

  • null — NullHibpCache<V> is the always-miss adapter used by HibpClient when the caller explicitly opts out of caching (`cac…

  • client — (no submodules)- options.schema — HibpClientOptionsSchema is the Z…

    • client — (no submodules)- options.schema — HibpClientOptionsSchema is the Z…
    • options.schema — HibpClientOptionsSchema is the Zod boundary for the HibpClient constructor. It is .strict() — unknown keys cause…
  • codes — Spec-canonical path for the HIBP error-code registry. This file is a thin re-export of the top-level `src/error-codes.t…

  • hibp-error — HibpError is the structured error class every failure path inside @suaveplan/hibp throws. It carries one of the sev…

  • observability — (no submodules)- metrics — (no submodules)- privacy — (no submodules)- assert-no-leak — (no submodules)- range — (no submodules)- constant-time-compare — constantTimeEqualsHex compares two hex suffixes without early-exit on the first mismatching character. It is used by…

    • differential-privacy — padPrefixes generates the decoy prefixes used by the differential-privacy mode of checkPassword. Given the real 5-h…
    • range-mode — Houses the hash-mode dispatch utilities used by checkPassword: hashSha1Prefix (SHA-1 via @noble/hashes/sha1), `ha…
    • range-stream-parse — Streams an HTTP response body line-by-line through a TextDecoderStream and a newline framer, yielding `{ suffix, coun…
    • range-strict-frame — parseRangeLine is the per-line framer used by streamParseRangeBody. It enforces the documented Pwned Passwords line…
    • range — (no submodules)- breach.schema — BreachSchema is the Zod schema mirroring the HIBP v3 Breach payload. It validates every documented field, so the re…
  • paste.schema — PasteSchema validates the HIBP v3 Paste payload returned by /pasteaccount/{email}. Older paste records can omit `…

  • pwned-result.schema — PwnedResultSchema validates the runtime shape of every checkPassword result. It is the final boundary between the i…

  • error-codes — Hosts the canonical HIBP_CODES registry — exactly seven error codes that classify every failure path inside `@suavepl…

  • lru — LruHibpCache<V> is the default cache for `H…

  • cache — (no submodules)- lru — LruHibpCache<V> is the default cache for `H…

  • lru — LruHibpCache<V> is the default cache for HibpClient. It holds up to LRU_DEFAULT_CAPACITY entries (10 000) with `L…

  • null — NullHibpCache<V> is the always-miss adapter used by HibpClient when the caller explicitly opts out of caching (`cac…

  • client — (no submodules)- options.schema — HibpClientOptionsSchema is the Z…

    • client — (no submodules)- options.schema — HibpClientOptionsSchema is the Z…
    • options.schema — HibpClientOptionsSchema is the Zod boundary for the HibpClient constructor. It is .strict() — unknown keys cause…
  • codes — Spec-canonical path for the HIBP error-code registry. This file is a thin re-export of the top-level `src/error-codes.t…

  • hibp-error — HibpError is the structured error class every failure path inside @suaveplan/hibp throws. It carries one of the sev…

  • observability — (no submodules)- metrics — (no submodules)- privacy — (no submodules)- assert-no-leak — (no submodules)- range — (no submodules)- constant-time-compare — constantTimeEqualsHex compares two hex suffixes without early-exit on the first mismatching character. It is used by…

    • differential-privacy — padPrefixes generates the decoy prefixes used by the differential-privacy mode of checkPassword. Given the real 5-h…
    • range-mode — Houses the hash-mode dispatch utilities used by checkPassword: hashSha1Prefix (SHA-1 via @noble/hashes/sha1), `ha…
    • range-stream-parse — Streams an HTTP response body line-by-line through a TextDecoderStream and a newline framer, yielding `{ suffix, coun…
    • range-strict-frame — parseRangeLine is the per-line framer used by streamParseRangeBody. It enforces the documented Pwned Passwords line…
    • range — (no submodules)- breach.schema — BreachSchema is the Zod schema mirroring the HIBP v3 Breach payload. It validates every documented field, so the re…
  • paste.schema — PasteSchema validates the HIBP v3 Paste payload returned by /pasteaccount/{email}. Older paste records can omit `…

  • pwned-result.schema — PwnedResultSchema validates the runtime shape of every checkPassword result. It is the final boundary between the i…

  • error-codes — Hosts the canonical HIBP_CODES registry — exactly seven error codes that classify every failure path inside `@suavepl…

  • lru — LruHibpCache<V> is the default cache for `H…

  • cache — (no submodules)- lru — LruHibpCache<V> is the default cache for `H…

  • lru — LruHibpCache<V> is the default cache for HibpClient. It holds up to LRU_DEFAULT_CAPACITY entries (10 000) with `L…

  • null — NullHibpCache<V> is the always-miss adapter used by HibpClient when the caller explicitly opts out of caching (`cac…

  • client — (no submodules)- options.schema — HibpClientOptionsSchema is the Z…

    • client — (no submodules)- options.schema — HibpClientOptionsSchema is the Z…
    • options.schema — HibpClientOptionsSchema is the Zod boundary for the HibpClient constructor. It is .strict() — unknown keys cause…
  • codes — Spec-canonical path for the HIBP error-code registry. This file is a thin re-export of the top-level `src/error-codes.t…

  • hibp-error — HibpError is the structured error class every failure path inside @suaveplan/hibp throws. It carries one of the sev…

  • observability — (no submodules)- metrics — (no submodules)- privacy — (no submodules)- assert-no-leak — (no submodules)- range — (no submodules)- constant-time-compare — constantTimeEqualsHex compares two hex suffixes without early-exit on the first mismatching character. It is used by…

    • differential-privacy — padPrefixes generates the decoy prefixes used by the differential-privacy mode of checkPassword. Given the real 5-h…
    • range-mode — Houses the hash-mode dispatch utilities used by checkPassword: hashSha1Prefix (SHA-1 via @noble/hashes/sha1), `ha…
    • range-stream-parse — Streams an HTTP response body line-by-line through a TextDecoderStream and a newline framer, yielding `{ suffix, coun…
    • range-strict-frame — parseRangeLine is the per-line framer used by streamParseRangeBody. It enforces the documented Pwned Passwords line…
    • range — (no submodules)- breach.schema — BreachSchema is the Zod schema mirroring the HIBP v3 Breach payload. It validates every documented field, so the re…
  • paste.schema — PasteSchema validates the HIBP v3 Paste payload returned by /pasteaccount/{email}. Older paste records can omit `…

  • pwned-result.schema — PwnedResultSchema validates the runtime shape of every checkPassword result. It is the final boundary between the i…

  • error-codes — Hosts the canonical HIBP_CODES registry — exactly seven error codes that classify every failure path inside `@suavepl…

  • cache — (no submodules)- lru — LruHibpCache<V> is the default cache for HibpClient. It holds up to LRU_DEFAULT_CAPACITY entries (10 000) with `L…

  • null — NullHibpCache<V> is the always-miss adapter used by HibpClient when the caller explicitly opts out of caching (`cac…

  • client — (no submodules) - client — (no submodules) - options.schema — HibpClientOptionsSchema is the Zod boundary for the HibpClient constructor. It is .strict() — unknown keys cause…

  • codes — Spec-canonical path for the HIBP error-code registry. This file is a thin re-export of the top-level `src/error-codes.t…

  • hibp-error — HibpError is the structured error class every failure path inside @suaveplan/hibp throws. It carries one of the sev…

  • observability — (no submodules) - metrics — (no submodules)- privacy — (no submodules) - assert-no-leak — (no submodules)- range — (no submodules) - constant-time-compare — constantTimeEqualsHex compares two hex suffixes without early-exit on the first mismatching character. It is used by…

    • differential-privacy — padPrefixes generates the decoy prefixes used by the differential-privacy mode of checkPassword. Given the real 5-h…
    • range-mode — Houses the hash-mode dispatch utilities used by checkPassword: hashSha1Prefix (SHA-1 via @noble/hashes/sha1), `ha…
    • range-stream-parse — Streams an HTTP response body line-by-line through a TextDecoderStream and a newline framer, yielding `{ suffix, coun…
    • range-strict-frame — parseRangeLine is the per-line framer used by streamParseRangeBody. It enforces the documented Pwned Passwords line…
    • range — (no submodules)- breach.schema — BreachSchema is the Zod schema mirroring the HIBP v3 Breach payload. It…
  • paste.schema — PasteSchema validates the HIBP v3 Paste payload returned by `…

  • pwned-result.schema — PwnedResultSchema validates the runtime shape of every checkPassword result. It is the final boundary between the i…

  • error-codes — Hosts the canonical HIBP_CODES registry — exactly seven error codes that classify every failure path inside `@suavepl…

Privacy contract

This package treats the password and its SHA-1 digest as secrets that must never leave the calling process.

  • Only the first five hex characters of the SHA-1 digest are transmitted to the Pwned Passwords range API (k-anonymity).
  • Every range request carries the mandatory Add-Padding: true header so Cloudflare appends decoy lines, defeating response-size correlation attacks.
  • The full password, the full hash, the API key, and the email are never logged and never appear in any thrown error's serialized form.
  • An opt-in assertOutgoingRequestSafe: true switch wraps every outgoing fetch with a byte-by-byte leak check so callers can catch a regression before it lands in production.

If any of those invariants ever break, that is a defect, not a feature request.

Install

bun add @suaveplan/hibp

Quick start

Check a single password against the range API

import { HibpClient } from "@suaveplan/hibp";

const client = new HibpClient();
const result = await client.checkPassword("hunter2");
// { pwned: true, count: 23_597 }

Pull the breach list for an account

import { HibpClient } from "@suaveplan/hibp";

const client = new HibpClient({ apiKey: process.env.HIBP_API_KEY! });
const breaches = await client.getBreachesForAccount("user@example.com");
// Breach[]

Differential-privacy mode

For deployments that want belt-and-braces defence on top of the standard Add-Padding padding, opt in to decoy prefixes. Every checkPassword call will dispatch the real 5-hex prefix plus N additional random sibling prefixes; only the real one's response is consulted.

const client = new HibpClient({
    differentialPrivacy: { extraPrefixes: 3 },
    rng: myInjectableRng,
});

Trade-offs:

  • Adds N extra round trips per check. Default off because the Add-Padding header already defeats the documented correlation attack for almost every deployment.
  • Decoy responses are discarded but still count against any caching layer's bandwidth budget. Tune extraPrefixes to match the operational cost.

Modules

Path Purpose
src/client/client.md HibpClient façade orchestrating range + breach + cache + observability
src/client/options.schema.md Zod boundary for constructor options
src/range/range.md k-anonymity range API client
src/range/range-mode.md SHA-1 / NTLM mode dispatch
src/range/range-stream-parse.md Streaming line parser
src/range/range-strict-frame.md Strict-mode line framing
src/range/constant-time-compare.md Constant-time suffix compare
src/range/differential-privacy.md Decoy prefix generator
src/breach/breach-client.md Authenticated breach + paste API client
src/breach/rate-limit-gate.md 1500 ms client-side rate-limit gate
src/breach/retry-after.md RFC 9110 Retry-After parser
src/cache/cache.md HibpCacheLike interface
src/cache/lru.md Default LRU cache (10 000 / 24 h)
src/cache/null.md Always-miss cache adapter
src/errors/codes.md Seven-code error registry
src/errors/hibp-error.md HibpError base class
src/error-codes.md Top-level code registration
src/observability/metrics.md OTel histograms + counters
src/privacy/assert-no-leak.md Outgoing-request leak detector
src/schemas/breach.schema.md BreachSchema Zod schema
src/schemas/paste.schema.md PasteSchema Zod schema
src/schemas/pwned-result.schema.md PwnedResultSchema Zod schema

Benchmarks

Benchmark Description
range-cold Cold range fetch + parse
range-warm-cache LRU hit path
range-parse-30kib Streaming parser throughput on a 30 KiB padded fixture

Run with bun run benchmark (see src/__benchmarks__/).

License

MIT