DomainViz – Universal Network Intelligence Powered by Cloudflare Workers

31 Jan 2026 • 7 min read

Project Overview

DomainViz is an open‑source platform that turns the fragmented world of WHOIS, RDAP, and DNS data into a single, easy‑to‑use service. Built on Cloudflare Workers, it lets you look up domains, IP addresses (IPv4 / IPv6), and Autonomous System Numbers (ASNs) from anywhere on the globe, store historical snapshots, and receive automated change alerts.

Why does it matter?

  • Security analysts can trace ownership changes instantly.
  • DevOps teams get reliable DNS records without juggling multiple APIs.
  • Researchers obtain bulk data and historical context without paying for third‑party services.

All of this runs on a serverless edge network, giving millisecond‑scale response times and global availability.


Key Features

FeatureWhat It Does
Universal Input SupportAccepts domains, IPv4, IPv6, and ASNs in a single endpoint; the system auto‑detects the type.
Bulk Lookups (≤ 50 items)One request can process up to 50 queries, returning per‑item status flags such as Ok, Locked, Hold, Pending.
Historical WHOIS / RDAPAuthenticated users can view time‑stamped snapshots of WHOIS/RDAP data, useful for ownership investigations.
Comprehensive IP WHOISReturns full entity details – contacts, addresses, phone numbers, and e‑mail – for IP blocks.
ASN EnrichmentProvides CIDR ranges and organization contacts for any ASN.
DNS Record QueriesA, AAAA, MX, NS, TXT, SOA, CAA and more are fetched via multi‑provider DoH (Cloudflare, Google, AdGuard).
Automated Monitoring & Change DetectionCron‑driven queues regularly re‑run lookups, diff snapshots, and fire alerts via e‑mail or webhooks.
HTTP Content MonitoringCaptures page HTML, runs a structural DOM diff, and (optionally) applies AI‑based semantic analysis.
Smart NotificationsThreshold‑based alerts (low / medium / high / critical) let you ignore noise and focus on real incidents.
Export & ShareResults can be downloaded as JSON or CSV, copied into Google Sheets, and shared via permalinks.
Password‑less AuthMagic‑link login keeps the experience frictionless while remaining secure.

Architecture Overview

A lightweight ASCII diagram that captures the core data flow (≈ 60 characters wide, ≤ 10 lines):

TEXT
+----------------+   +----------------+   +-----------------+
| IANA Bootstrap |→→|  RDAP Servers  |→→| Cloudflare DoH  |
|   Service      |   | (1,192 TLDs)   |   |   (HTTPS)       |
+----------------+   +----------------+   +-----------------+
        |                  |                     |
        +--------+---------+---------------------+
                 |
                 v
+-------------------------------------------------------+
|               Edge Workers (Hono API)                |
|  4‑Tier RDAP fallback • DNS resolution • Queue Mgmt   |
+-------------------------------------------------------+
        |                     |
        v                     v
+----------------+   +----------------+   +----------------+
|     D1 DB      |←→|   KV Cache     |←→|   R2 Buckets   |
| Authoritative  |   | Fast look‑ups |   | Snapshots &   |
| watches, users |   +----------------+   |   HTML files   |
+----------------+                         +----------------+
        |
        v
+-------------------+
|   SvelteKit UI    |
|   (Web App)       |
+-------------------+

The edge worker layer (Hono) handles HTTP routing, performs the 4‑tier RDAP fallback, writes snapshots to R2, and queues monitoring jobs. D1 stores the authoritative watch definitions, KV provides low‑latency caching, and R2 holds historic data.


How It Works

1. Input Detection & Routing

When a request hits /api/lookup, the worker inspects the query string to decide which lookup path to follow:

TS
// src/routes/lookup.ts
import { detectInput } from "./utils";

export async function onRequest(context) {
  const url = new URL(context.request.url);
  const query = url.searchParams.get("query");
  const type = detectInput(query); // "domain" | "ipv4" | "ipv6" | "asn"

  switch (type) {
    case "domain":
      return lookupDomain(query, context.env);
    case "ipv4":
    case "ipv6":
      return lookupIP(query, context.env);
    case "asn":
      return lookupASN(query, context.env);
  }
}

detectInput uses simple regex checks and returns a normalized type, allowing a single endpoint to serve all universal inputs.


2. 4‑Tier RDAP Fallback

The core RDAP routine first checks the KV cache, then the IANA Bootstrap list, then the public rdap.org service, and finally falls back to a raw WHOIS socket for legacy TLDs.

TS
// src/services/rdap.ts
export async function rdapLookup(query: string, env: Env) {
  // 1️⃣ KV fast‑path cache
  const cached = await env.KV.get(query);
  if (cached) return JSON.parse(cached);

  // 2️⃣ IANA Bootstrap – find the authoritative RDAP server
  const bootstrap = await fetch(
    `https://data.iana.org/rdap/${query.includes('.') ? "domains" : "ips"}.json`
  ).then(r => r.json());

  const rdapUrl = bootstrap.services?.[0]?.[1]?.[0];
  if (!rdapUrl) throw new Error("No RDAP service found");

  // 3️⃣ Direct RDAP request
  try {
    const resp = await fetch(`${rdapUrl}/${encodeURIComponent(query)}`);
    const data = await resp.json();
    await env.KV.put(query, JSON.stringify(data), { expirationTtl: 86400 });
    return data;
  } catch (e) {
    // 4️⃣ WHOIS socket fallback (only for legacy TLDs)
    return whoisFallback(query, env);
  }
}

Key points

  • KV entries expire after 24 h to keep data fresh.
  • The IANA bootstrap file is fetched on‑demand; the list is small (< 10 KB).
  • whoisFallback opens a TCP socket to the legacy WHOIS server, parses the plain‑text response, and normalizes it to the same JSON shape.

3. Monitoring Queue & Cron Scheduler

A daily (or user‑defined) Cron trigger runs ScheduleManager.getDueWatches(), pushes each due watch onto the WATCH_CHECK_QUEUE, and lets a separate consumer handle the heavy lifting.

TS
// src/cron/schedule.ts
export async function getDueWatches(env: Env) {
  const now = new Date().toISOString();
  const { results } = await env.DB.prepare(
    "SELECT id, query FROM watches WHERE next_check <= ?"
  )
    .bind(now)
    .all();
  return results;
}

// Enqueue
for (const w of await getDueWatches(env)) {
  await env.WATCH_CHECK_QUEUE.send(JSON.stringify(w));
}

The watch‑check consumer reads the queue, runs rdapLookup (or DNS lookup), stores the new snapshot in R2, and calls compareSnapshots (see next section).


4. Change Detection & Notification

TS
// src/services/compare.ts
export function compareSnapshots(oldSnap: any, newSnap: any) {
  const diffs = [];

  // Simple deep‑diff for illustrative purposes
  for (const key of Object.keys(newSnap)) {
    if (JSON.stringify(oldSnap[key]) !== JSON.stringify(newSnap[key])) {
      diffs.push({ field: key, old: oldSnap[key], new: newSnap[key] });
    }
  }

  const severity = diffs.length > 10 ? "high" : diffs.length > 3 ? "medium" : "low";
  return { diffs, severity };
}

If severity meets the user’s alert threshold, the platform dispatches an e‑mail or webhook payload:

TS
// src/services/notify.ts
export async function sendAlert(watch, result, env) {
  const payload = {
    watchId: watch.id,
    query: watch.query,
    severity: result.severity,
    changes: result.diffs,
  };
  await fetch(env.WEBHOOK_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });
}

5. HTTP Content Monitoring (Optional)

When a watch includes type: "http", the worker fetches the page, stores the raw HTML in R2, and runs a DOM‑diff library (e.g., diff-dom). The resulting diff is then fed into the same severity engine as above, allowing security teams to detect defacements or phishing page changes.


Getting Started (Quick Start)

  1. Clone the repo

    BASH
    git clone https://github.com/xxdesmus/whoisdns.git
    cd whoisdns
  2. Install dependencies (workspace uses pnpm)

    BASH
    pnpm install
  3. Run the API locally

    BASH
    cd apps/api
    npm run dev        # Starts a Workers dev server on http://127.0.0.1:8787
  4. Run the frontend

    BASH
    cd ../web
    npm run dev        # Vite dev server at http://localhost:5173
  5. Deploy (requires a Cloudflare account & API token)

    BASH
    cd apps/api
    npm run deploy    # Deploys the Hono worker
    cd ../web
    pnpm run build && pnpm run deploy   # Deploys the SvelteKit site to Cloudflare Pages

The README also lists the full command matrix (npm run test, pnpm run ci, etc.) for CI pipelines.


Recent Developments

  • Authoritative D1 migration – The lazy KV‑to‑D1 migration has been removed; D1 now stores the single source of truth for watches and user data, simplifying consistency guarantees.
  • Bulk‑lookup race fix – Concurrency bugs in the 50‑item batch path were resolved, delivering stable per‑item status reporting.
  • Mobile navigation polish – Overlap issues on small screens were fixed, improving the touch experience.
  • Admin finalize endpoint – A new API route lets administrators clean up stuck bulk jobs, reducing manual intervention.

These updates were shipped in the latest releases and are reflected in the live service.


Conclusion

DomainViz turns the chaotic world of WHOIS, RDAP, and DNS into a cohesive, edge‑native platform. By unifying universal lookups, historical snapshots, and automated monitoring under a single, password‑less UI, it empowers security teams, developers, and researchers to act faster and with greater confidence. The serverless architecture—leveraging Cloudflare Workers, D1, KV, and R2—delivers global performance while keeping operational overhead low.

Explore the live web app at https://domainviz.org , experiment with the public API at https://api.domainviz.org , and dive into the source code on GitHub to contribute or customize the platform for your own workflows.

Start searching

Enter keywords to search articles.