← back to Domain Sniper

check.js

81 lines

#!/usr/bin/env node
// Leak-minimal single-domain availability check.
// Method: Cloudflare 1.1.1.1 DoH (DNS-over-HTTPS), query type=NS.
// Avoids: WHOIS, RDAP at registry, GoDaddy availability API, registrar search boxes —
// all known leak vectors after 3 confirmed snipes (callr.app, callr.co, butlr.app).

const https = require('https');
const { isOwned } = require('./owned-check');

const target = (process.argv[2] || '').trim().toLowerCase();
if (!target || !target.includes('.')) {
  console.error('usage: node check.js <domain>');
  process.exit(2);
}

function doh(name, type = 'NS') {
  return new Promise((resolve, reject) => {
    const url = `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(name)}&type=${type}`;
    https
      .get(url, { headers: { Accept: 'application/dns-json' } }, (res) => {
        let data = '';
        res.on('data', (c) => (data += c));
        res.on('end', () => {
          try {
            resolve(JSON.parse(data));
          } catch (e) {
            reject(new Error(`bad DoH response: ${data.slice(0, 200)}`));
          }
        });
      })
      .on('error', reject);
  });
}

(async () => {
  // Two parallel queries — NS (does the TLD know about it) + SOA (does it have a zone of its own).
  // Both via Cloudflare. We do NOT call WHOIS/RDAP under any circumstance.
  const [ns, soa] = await Promise.all([doh(target, 'NS'), doh(target, 'SOA')]);

  // DNS Status codes:
  //   0 = NOERROR — TLD knows the name (registered, possibly parked)
  //   2 = SERVFAIL — temporary, retry
  //   3 = NXDOMAIN — TLD doesn't know the name (unregistered)
  const nxNs = ns.Status === 3;
  const nxSoa = soa.Status === 3;
  const hasNs = !!(ns.Answer && ns.Answer.length);
  const hasSoa = !!(soa.Answer && soa.Answer.length);

  // Available if BOTH NS + SOA come back NXDOMAIN. Either NOERROR signals the TLD recognizes it.
  const available = nxNs && nxSoa;

  const owned = isOwned(target);
  const result = {
    domain: target,
    available,
    owned,
    confidence: available ? 'high' : hasNs || hasSoa ? 'high' : 'medium',
    signals: {
      ns_status: ns.Status,
      soa_status: soa.Status,
      has_ns: hasNs,
      has_soa: hasSoa,
      ns_records: hasNs ? ns.Answer.map((a) => a.data) : [],
    },
    method: 'cloudflare-doh-ns-soa',
    leak_minimal: true,
    timestamp: new Date().toISOString(),
    advice: owned
      ? 'OWNED — this domain is in data/owned.json (you already have it). DoH still ran.'
      : available
      ? 'AVAILABLE — register within minutes via API. Do not type into registrar search boxes.'
      : 'TAKEN — TLD recognizes the name. Move on to the next candidate.',
  };

  console.log(JSON.stringify(result, null, 2));
  process.exit(available ? 0 : 1);
})().catch((err) => {
  console.error(JSON.stringify({ error: err.message, domain: target }, null, 2));
  process.exit(3);
});