@capitalthought/marc-zone-check
v0.1.1
Published
Lightning-fast domain-registration check via ICANN CZDS zone files. Used by the /domain-search Claude Code skill as Tier 1.5 between DoH NS scans and WHOIS+RDAP.
Maintainers
Readme
@capitalthought/marc-zone-check
Lightning-fast domain-registration check via ICANN CZDS zone files.
Powers the Tier 1.5 step in Josh's /domain-search Claude Code skill — between Cloudflare DoH NS scanning (Tier 1) and per-domain WHOIS+RDAP (Tier 2). For TLDs you have CZDS approval for, the answer is authoritative (every registered domain appears in the registry's zone file, even those without DNS configured — solves the "registered without nameservers" false-positive class that DNS-only checks miss).
Install
npm i -g @capitalthought/marc-zone-check
# or one-shot:
npx -y @capitalthought/marc-zone-check --domain acme.appGet CZDS access
CZDS is ICANN's Centralized Zone Data Service. You need an approved account for each TLD whose zone files you want to read. Sign up at https://czds.icann.org/, request access per-TLD (most approvals take 1-30 days). Once approved, you have:
- Username + password — your ICANN account creds
- 24h JWT — minted via
POST https://account-api.icann.org/api/authenticate
CLI
# Auth via env: pre-minted JWT
export CZDS_JWT='eyJraWQi...'
marc-zone-check --domain acme.app --domain multipov.dev
# Auth via env: username + password (mints fresh JWT per run)
export CZDS_USERNAME='[email protected]'
export CZDS_PASSWORD='...'
marc-zone-check --domains "acme.app,multipov.dev,foo.xyz"
# Bare positional args also work
marc-zone-check acme.app multipov.devOutput (JSON array on stdout):
[
{"fqdn":"acme.app","tld":"app","registered":true,"source":"czds_zone"},
{"fqdn":"multipov.dev","tld":"dev","registered":false,"source":"czds_zone"},
{"fqdn":"foo.com","tld":"com","registered":null,"source":"no_coverage_too_big","compressed_bytes":26000000000}
]-v adds per-step progress to stderr.
Library
import {
ZoneChecker,
createCzdsHttpFetcher,
mintCzdsJwt,
} from '@capitalthought/marc-zone-check';
const jwt = await mintCzdsJwt({ username: '...', password: '...' });
const fetcher = createCzdsHttpFetcher({ jwt });
const checker = new ZoneChecker({ fetcher });
const result = await checker.check('acme.app');
// → { fqdn: 'acme.app', tld: 'app', registered: true, source: 'czds_zone' }source field
| Value | Meaning |
|---|---|
| czds_zone | Authoritative — TLD is in CZDS coverage. registered: true|false. |
| no_coverage_no_approval | You are not approved for this TLD (or it's not in CZDS at all). Fall through to WHOIS. |
| no_coverage_too_big | TLD zone exceeds the in-memory cap (default 200 MB compressed — covers most gTLDs but excludes .com, .net, .org, .info). For these, use checker.checkFiltered() which streams without caching, OR fall through to WHOIS. compressed_bytes is populated. |
| no_coverage_invalid_fqdn | Input has no dot or is malformed. |
| fetch_failed | HTTP / parse / network error. error field is populated. |
Memory model
Two parse strategies live alongside each other:
Full-Set caching (
checker.check()) — stream-parse the zone file once, cache the fullSet<string>of registered SLDs in memory, then O(1) lookups for the lifetime of the process. Used by default. Capped at 200 MB compressed input (configurable viacreateCzdsHttpFetcher({ maxCompressedBytes })) — over that,no_coverage_too_big.Filtered streaming (
checker.checkFiltered(fqdns)) — stream-parse the zone, only retaintrue/falseanswers for the specific FQDNs you asked about. Memory is O(N candidates), not O(M registered SLDs). Use this for.com/.net/.orgwhere the full-Set variant would OOM. NB: every call streams fresh; no cache.
Most use cases want the default. Mega-TLDs need the filtered path.
Live test (.pro is ~33 MB compressed)
$ time marc-zone-check -v --domain marc.pro --domain zzznever12345.pro
[jwt] using $CZDS_JWT
[check] marc.pro → TAKEN_ZONE
[check] zzznever12345.pro → UNREG_ZONE
[{"fqdn":"marc.pro","tld":"pro","registered":true,"source":"czds_zone"},
{"fqdn":"zzznever12345.pro","tld":"pro","registered":false,"source":"czds_zone"}]
real 0m6.0svs. ~20s for the equivalent WHOIS+RDAP fan-out.
License
MIT. (c) Capital Thought, LLC.
