A static-first domain checking application built with semantic HTML, native CSS, vanilla JavaScript ES modules and an included native Node.js backend. The frontend publishes directly from public/ to GitHub Pages and can operate independently or delegate authoritative checks to the backend.
- Accepts pasted domains, URLs, TXT files and CSV files.
- Normalises hostnames, removes duplicates and rejects unsafe input.
- Supports client, hybrid and server execution modes.
- Lets users select Automatic, Cloudflare, Google, custom DNS-over-HTTPS or backend DNS resolution.
- Supports both provider-compatible JSON DoH and RFC 8484 wire-format DoH.
- Tests the configured backend and DNS resolver from the interface.
- Blocks private, loopback, link-local, multicast, documentation and reserved DNS answers before HTTPS probing.
- Performs optional HTTPS probes using the browser or configured backend.
- The native backend follows validated redirects, records TLS certificate metadata and returns selected response headers.
- The backend supports allowlisted DNS over UDP, TCP and DNS-over-TLS without Docker.
- Supports concurrency limits, retries, rate limiting, pause, resume and stop.
- Provides search, filtering, sorting, resolver provenance and selectable result rows.
- Generates CSV and JSON reports in the browser.
- Saves only user preferences in
localStorage.
| Mode | DNS | HTTPS | Backend required |
|---|---|---|---|
client |
Browser-accessible DoH | Browser fetch() |
No |
hybrid |
Browser DoH by default; optional backend DNS | Backend API | Yes |
server |
Backend API | Backend API | Yes |
The default GitHub Pages deployment uses client mode and remains fully usable without a backend.
Browser security restrictions deliberately limit what JavaScript can observe:
- CORS can hide response status and headers.
- A failed browser request cannot distinguish downtime from TLS, policy, CORS or network failure.
- Browsers cannot send traditional UDP/TCP DNS queries to a user-specified server.
- TLS certificate chains, complete redirects and server headers are not generally exposed.
A failed fetch() is therefore never labelled as definitive proof that a site is dead.
The frontend offers:
| Resolver | Client | Hybrid | Server |
|---|---|---|---|
| Automatic: Cloudflare β Google | β | β | β |
| Cloudflare | β | β | β |
| β | β | β | |
| Custom DoH β RFC 8484 wire | β * | β * | β ** |
| Custom DoH β JSON API | β * | β * | β ** |
| Backend DNS β UDP/TCP/DoT | β | β | β |
* The custom endpoint must use HTTPS, trust a browser-recognised certificate and allow the site origin through CORS when queried directly by the browser.
** Backend custom resolvers are disabled by default and require an operator allowlist.
Every DNS result records the resolver label, transport, format, fallback attempts and execution source.
| Result | Interpretation |
|---|---|
HTTPS_READABLE |
The selected execution provider obtained a readable HTTPS response. |
HTTPS_OPAQUE |
A browser no-CORS request completed, but details remain hidden. |
DNS_ONLY |
DNS resolved while HTTPS remained inconclusive or disabled. |
NO_WEB_ADDRESS |
No usable public A or AAAA address was returned. |
NXDOMAIN |
The selected resolver reported that the name does not exist. |
BLOCKED_TARGET |
DNS returned a private, reserved or otherwise disallowed address. |
INVALID |
The submitted value failed input validation. |
CHECK_ERROR |
A resolver, browser or backend failure prevented classification. |
CANCELLED |
The scan was stopped before completion. |
public/ is the complete production website root. Nothing outside it is required for client mode.
domain-checker/
βββ public/ # Complete GitHub Pages artefact
β βββ index.html
β βββ .nojekyll
β βββ manifest.webmanifest
β βββ css/
β βββ icons/
β βββ js/
β βββ adapters/
β βββ config/
β βββ core/
β βββ export/
β βββ providers/
β β βββ dns/
β β βββ execution/
β βββ services/
β βββ storage/
β βββ ui/
β βββ utilities/
βββ backend/ # Native Node.js API and deployment files
β βββ src/ # DNS, HTTPS, policy and API modules
β βββ config/ # Environment template
β βββ deploy/ # systemd, nginx and installer
β βββ README.md
βββ tests/ # Frontend and backend automated tests
βββ scripts/ # Validation and local development tools
βββ docs/
β βββ BACKEND_API.md # Implemented backend contract
β βββ adr/ # Architecture decision records
βββ .github/workflows/ # CI and GitHub Pages deployment
βββ Dockerfile # Optional backend container image
βββ compose.yaml # Hardened local container deployment
βββ ARCHITECTURE.md
βββ CHANGELOG.md
βββ README.md
βββ package.json
The full component boundaries, data models, security controls and AI-agent handover rules are defined in ARCHITECTURE.md.
- Node.js 22 or newer for repository tooling and the native backend.
- A current Chromium, Firefox or Safari browser.
npm ci
npm run checkThis runs formatting checks, JavaScript safety linting, 85 automated frontend/backend tests and public/ self-containment validation.
npm run serveOpen:
http://127.0.0.1:4173/
To simulate a GitHub Pages repository path:
BASE_PATH=/domain-checker/ npm run serveThen open:
http://127.0.0.1:4173/domain-checker/
- Push the repository with
mainas the default branch. - Open Settings β Pages.
- Set Build and deployment β Source to GitHub Actions.
- Push to
mainor run the deployment workflow manually.
The workflow validates the repository and uploads only ./public as the Pages artefact.
The repository includes the API documented in docs/BACKEND_API.md:
GET /api/v1/health
POST /api/v1/check
###Β Run it without Docker:
cp backend/config/domain-checker-backend.env.example backend.env
set -a
. ./backend.env
set +a
npm run backend:startTest it:
curl -sS http://127.0.0.1:8080/api/v1/healthcp backend/config/domain-checker-backend.env.example backend.env
docker compose up --build -d
docker compose ps
curl -sS http://127.0.0.1:8080/api/v1/healthThe default environment example is production-safe and permits public targets only. For an explicitly allowlisted private network, start from:
cp backend/config/domain-checker-backend.private-lan.env.example backend.envThe Compose service is read-only, runs as an unprivileged user with all Linux capabilities dropped,
and publishes to loopback by default. Set BACKEND_BIND_ADDRESS only when a reverse proxy on another
host must reach the container. See backend/README.md for image, configuration and
shutdown details.
Install it as a hardened systemd service:
sudo backend/deploy/install-native.shThe secure default binds to loopback. Configure nginx, TLS, ALLOWED_ORIGINS, rate limits and resolver allowlists before exposing it. Full native deployment instructions are in backend/README.md.
npm test
npm run test:unit
npm run test:integration
npm run test:e2e
npm run backend:testThe suite covers:
- URL and hostname parsing;
- duplicate removal and internationalised domains;
- private and reserved IPv4/IPv6 classification;
- queue concurrency, retry, pause, resume and cancellation;
- automatic and explicit DNS provider selection;
- custom JSON and RFC 8484 wire-format DoH;
- backend health and check response normalisation;
- native API routing, CORS, request validation and secure defaults;
- UDP/TCP DNS framing and native resolver execution;
- backend redirect validation, target pinning and TLS metadata;
- client, hybrid and server execution routing;
- readable, opaque and timed-out HTTPS probes;
- CSV/JSON provenance and formula protection;
- GitHub Pages repository-subpath hosting.
Automated tests use mocks and loopback-only test services; they do not require live Internet access.
- User-controlled values are rendered with safe DOM APIs rather than
innerHTML. - Submitted IP literals and private/reserved DNS answers are blocked.
- Browser and backend requests omit cookies and referrer information.
- Custom service URLs reject embedded credentials and URL fragments.
- Browser custom DoH requires HTTPS.
- Backend custom resolvers are disabled unless explicitly enabled and allowlisted.
- Backend HTTPS connections are pinned to freshly validated public addresses.
- Redirect destinations are re-resolved and revalidated before connection.
- Client probe redirects use manual handling.
- No runtime CDN dependencies are used.
- No API keys or secrets belong in
public/. - CSV output neutralises spreadsheet formulas.
- Scan history is not persisted.
- No analytics or central telemetry is enabled.
DNS queries are visible to the selected resolver. HTTPS probes are visible to target infrastructure. Hybrid and server modes also disclose submitted domains to the configured backend.
Agents must read, in order:
ARCHITECTURE.md;README.md;- applicable ADRs;
docs/BACKEND_API.mdfor backend-facing work;- relevant tests;
- modules directly involved in the change.
Architectural changes require an ADR. Behavioural changes require tests.
- Authenticated API profiles suitable for public multi-user deployments.
- RDAP and broader security-header enrichment.
- Opt-in IndexedDB scan history and scan comparison.
- Table virtualisation for very large result sets.
- Browser automation with Playwright.
Released under the MIT Licence.