Skip to content
Code Recycle

Component · for humans & their agents

Domain Exposure Watch

basic checks · first-partyactively maintained$0 during beta (was $39)

Which addresses on YOUR domains are in breaches, and who has registered a look-alike of your domain.

by saltyhash · Code Recycle admin

Get it free — beta

Every claim on this page is refundable if it is untrue — refund policy.

Verified: 19 tests · 6/6 mutations caught

| lane | input | needs a key | |---|---|---| | `registeredLookalikes(domain)` | a domain you own | **no** — public DNS-over-HTTPS | | `breachedOwnedAccount(email, ownedDomains)` | an address **on a domain you declared you control** | yes, your own key |

Two lanes

| lane | input | needs a key | |---|---|---| | `registeredLookalikes(domain)` | a domain you own | no — public DNS-over-HTTPS | | `breachedOwnedAccount(email, ownedDomains)` | an address on a domain you declared you control | yes, your own key |

The constraint is in the code, not in the terms

An unconstrained "look up any email address" function is a doxxing tool wearing a security jacket. The defensive use — *which of my staff addresses are in breaches, so I can force resets* — only ever needs addresses on domains the operator already controls.

That is also the boundary the breach service itself draws: its bulk surface is gated behind proving you control a domain before it tells you anything about the addresses on it.

So `ownedDomains` is a required argument, not an option with a permissive default:

```ts await breachedOwnedAccount("stranger@gmail.com", ["acme.com"], { apiKey }); // { ok: false, reason: "not_an_owned_domain", detail: "refusing to look up an address // outside the domains you declared you control..." } ```

A refusal is a first-class typed result, not a thrown error someone catches and ignores. It happens *before* the API-key check — so the message is about the address rather than sending you off to get a key you still cannot use — and *before any network call*, which a test enforces with a `fetch` that throws if reached.

There is deliberately no flag to disable it. A switch labelled "allow any address" is the whole tool, and shipping one would make every sentence above decorative.

Ownership falls on a dot. `notacme.com` ends with `acme.com` and belongs to somebody else — a suffix match would hand an attacker lookup on every address at a domain registered specifically to end in yours. A deliberate defect that weakens exactly this is caught by the suite.

"Could not determine" is never reported as "clean"

  • `breaches: []` — asked, and the answer was nothing
  • `breaches: null` — rate-limited, timed out, or the API errored

Collapsing those two is how an outage becomes a clean bill of health, and it is the single most consequential thing a monitor of this kind gets wrong. The service reports "no breaches" as an HTTP 404, which is easy to mistake for a failure; that mapping is pinned by test in both directions.

The keyless lane

Generates common look-alike variants of your domain — one-character omissions, adjacent transpositions, doublings, homoglyph swaps (`o→0`, `l/i→1`, `e→3`), and the same label on other common TLDs — then DNS-resolves each. A variant that resolves is registered and worth a glance: defensive registration, parking, or someone impersonating you. Bounded to 40 variants so the fan-out cannot run away.

It takes a domain and never a person, which is why this lane needs no constraint at all — every question it asks is a public DNS lookup anyone can make.

Not affiliated

Built to work *with* a public breach-notification API, using your own key, under that service's terms. Not affiliated with or endorsed by it.

Proof

19 tests, 6/6 deliberate defects caught — including the one that matters most: removing the ownership boundary entirely.

Delivery

Source delivered as a private repository invite within 24 hours of purchase. Single-product commercial license: use and modify in any number of products; no redistribution or resale of the source.

Interface

What you call, and what comes back. Types and signatures only — the implementation ships with the source.

  export function addressDomain(email: string): string | null;
  export function isOwnedAddress(email: string, ownedDomains: readonly string[]): boolean;
  export async function breachedOwnedAccount( email: string, ownedDomains: readonly string[], opts: ExposureOptions = {}, ): Promise<BreachLookup>;
  export async function registeredLookalikes( domain: string, opts: ExposureOptions = {}, ): Promise<string[]>;
  export function parseList(raw: string): string[];
  export function typosquatVariants(domain: string): string[];
  export type LookupRefusal = "not_an_owned_domain" | "no_api_key" | "malformed_address";
  export type BreachLookup = | { ok: true;

01Capabilities

Does

  • + Input validation
  • + Domain parsing
  • + Compliance audit

Doesn’t

  • No exclusions declared

02Requirements & stack

Depends on

No declared dependencies

Credentials needed

None declared

Stack

typescript

03Community

No endorsements yet

No verified confirmations yet — be the first.

Confirmations come from verified purchasers, installers, vetted reviewers, or an installation outcome your org reported through the agent tools. They grade quality — security is verified separately, and community votes can never override the security gate.

Open an issue

Sign in to confirm — weight comes from verified usage, not vote count.

0 open · 0 answered · 0 fixed · 1 said it worked

04Trust Passport

Full passport →
–/100

0/0 automated components pass. An automated score is never a security guarantee.

✓ Basic checks · first-partyreviewed Sep 20, 2026 · re-verification due Dec 19, 2026
  • publisher identity Publisher status verified; 1 verification(s) on file
  • malicious pattern scan No known malicious-behavior patterns across 9 source file(s) plus listing text
  • capability contract 2 undeclared (0 credential-class): haveibeenpwned.com, dns.google
  • agent safety scan No injection patterns in agent-readable content
  • provenance No release signature or provenance attestation
  • behavioral sandbox Not performed in this environment — requires the production isolated runner (docs/sandbox-requirements.md). No untrusted code is ever executed on the application host.

Every listing must pass this review before it can be sold, and it is re-run on every release. Verification describes what we checked — it is not a guarantee that the software is safe.

VersionChannelReleasedNotes
1.0.0stableAug 10, 2026First public release.