dmcheck

May 22, 2026 · View on GitHub

Languages: English | 简体中文

dmcheck.app — Free domain availability checker powered by WHOIS & RDAP.

Enter a keyword, instantly check whether domains across multiple TLDs are available — results stream back in real time.

Product Experience Principles

dmcheck is designed for fast, repeated domain screening: enter one keyword, scan a focused set of TLD results, inspect details only when needed, and optionally create a compact result image for sharing.

  • Feels fast first — the interface stays quiet, results stream in place, cached data is used where safe, and heavy detail queries happen only after a row is opened.
  • Simple input, precise control — the primary path is a single keyword input; TLD settings use a long text area so users can paste, edit, remove, and reorder suffixes without checkbox fatigue.
  • Scannable results — available domains are grouped first after completion, each row shows the domain and one clear result, registered domains show the registration date, and very recent registrations use relative labels such as today, yesterday, or N days ago.
  • Details stay out of the way — registered, reserved, and unknown domains open a detail drawer with cached preview first and live WHOIS/RDAP details after click; website screenshot and favicon loading remain on demand.
  • Sharing is a separate flow — the main page width is optimized for use, not screenshots. The result image flow generates a narrow PNG, previews it in a dialog, then offers copy, download, and system share actions.
  • Homepage stays focused — FAQ content lives on a dedicated page while homepage SEO is handled through metadata, structured data, canonical/hreflang links, robots, and sitemap files.
  • Efficient visual tone — the UI should feel minimal, technical, and quick, with Graphite/Carbon themes, restrained motion, no broad gradients, and no decorative distractions.
  • Debuggable in production — the footer and app-version meta tag expose the deployed version so bug reports can be tied to a specific build.

Features

  • Real-time streaming — results appear one by one via SSE, no waiting for all queries to finish
  • WHOIS + RDAP — direct queries to registry WHOIS servers (port 43) with RDAP fallback via IANA bootstrap
  • 83 TLDs preconfigured with WHOIS servers; 1000+ TLDs supported via RDAP fallback
  • Customizable TLD list — users can edit their TLD list in-browser (saved to localStorage)
  • Domain detail panel — registration dates, registrar, DNS servers, status codes, raw WHOIS, optional site screenshot & favicon
  • Result image export — compact PNG preview with copy, download, and system share actions
  • Optional registrar links and price comparison — when enabled, available domains can show configured registrar links, first-year USD reference prices, and a detail-drawer comparison
  • Reserved domain detection — identifies registry-reserved domains separately from registered or available
  • Multi-language — English (default), 中文, 日本語, 한국어, Español
  • Redis caching — optional; gracefully degrades to no-cache mode
  • Rate limiting — IP-based token bucket to prevent abuse
  • Single binary — all static assets embedded via go:embed

Quick Start

Prerequisites

  • Go 1.21+
  • (Optional) Node.js 18+ for refreshing registrar price references
  • (Optional) Redis for caching

Run locally

go run .

The server starts on http://localhost:3300 by default.

Build

go build -o dmcheck .
./dmcheck

Configuration

Config files

FileDescription
config/whois-servers.jsonTLD → WHOIS server mapping
config/default-tlds.jsonDefault TLD list shown to users
config/registrar-prices.jsonOptional registrar link templates and configured TLD price references; used only when REGISTRAR_PRICES_ENABLED=true

dmcheck can run as a plain domain availability checker with no registrar data. This is the default mode and only requires Go. In this mode, config/registrar-prices.json is ignored, API responses omit registration_options, and the UI does not show registrar actions or price comparison.

Set REGISTRAR_PRICES_ENABLED=true to enable the optional registrar module. When enabled, available-domain results include registrar search/register links, first-year USD reference prices where available, and the comparison drawer in the UI.

config/registrar-prices.json contains two related but separate things: enabled registrar channels for outbound registration/search links, and automated price rows for the comparison UI. A registrar can be available as a link-only channel even when it is not used for automated pricing.

Current coverage as of config/registrar-prices.json updated_at=2026-05-15: 5 enabled registrar channels, 833 priced TLDs total, including 204 multi-label TLDs.

RegistrarUser-facing channelAutomated price rowsUpdate modeNotes
Cloudflare RegistrarRegistration dashboard link0Link onlyCloudflare publishes registrar positioning and supported TLD information, but not a broad public price table suitable for unattended refreshes.
PorkbunRegistration/search link and price comparison632Automated from official public pricing pageUsed for broad TLD coverage.
NamecheapRegistration/search link0Link onlyPublic pricing pages block scripted refresh and the official pricing API requires account credentials.
SpaceshipRegistration/search link0Link onlyA public domain pricing page exists at https://www.spaceship.com/domains/ and exposes more rows through a "See more" UI, but the unattended updater receives Cloudflare verification from Node, curl, and headless Chrome. The official API also requires key/secret credentials, so this stays link-only until there is a stable non-interactive source.
DynadotRegistration/search link and price comparison810Automated from official public pricing pageUsed for broad TLD coverage, including many multi-label TLDs.

NameSilo and GoDaddy have been evaluated but are not enabled channels in the current config. They are kept out until we have a stable unattended source or an intentional credential-backed integration.

Updating registrar price references

node scripts/update-registrar-prices.mjs --date=YYYY-MM-DD

Update mechanism:

  • Run the script from the repo root weekly and before each release that changes registrar behavior.
  • The script fetches the official Porkbun and Dynadot pricing pages, parses first-year registration and renewal prices, removes price rows from non-automated sources, sorts the generated data, and writes config/registrar-prices.json.
  • The script refuses to overwrite the config if Porkbun returns fewer than 100 rows or Dynadot returns fewer than 300 rows, which helps avoid replacing the price table with an anti-bot or transient error page.
  • Review the printed coverage counts after every refresh, then commit the generated config/registrar-prices.json only when both automated sources pass their guards.

If a source starts failing consistently, leave it out of automated pricing until it has a stable public feed again.

Providers intentionally not automated or priced: Namecheap, NameSilo, Spaceship, and GoDaddy require account/API credentials, browser-only interaction, or block scripted access to public pricing pages; Cloudflare publishes registrar positioning and supported TLD information but not a broad public price table suitable for unattended refreshes.

Environment variables

All runtime environment variables are read in config.go and stored in the global AppConfig. Other packages should read configuration from AppConfig instead of calling os.Getenv directly.

For systemd deployments, keep overrides in one server-side file: /opt/dmcheck/dmcheck.env. The provided deploy/dmcheck.service loads it through EnvironmentFile. This file is not committed or deployed by GitHub Actions, so create it once on the server or copy a local private copy securely.

VariableDefaultDescription
PORT3300HTTP server port
REDIS_ADDRlocalhost:6379Redis address (set empty to disable)
RATE_LIMIT2Requests per second per IP
RATE_BURST5Rate limiter burst size
AVAILABLE_CACHE_TTL0Cache TTL for available domains; 0 disables available-result caching
REGISTERED_CACHE_TTL2160hMax cache TTL for registered/reserved domains; registered domains are refreshed before expiry
CACHE_TTL(empty)Legacy alias for AVAILABLE_CACHE_TTL
REGISTRAR_PRICES_ENABLEDfalseEnables registrar links and price comparison from config/registrar-prices.json
GA_ID(empty)Google Analytics Measurement ID (omit to disable)
SITE_URLhttps://dmcheck.appCanonical site URL used in rendered SEO metadata
APP_VERSIONbuild metadataOptional fixed version string shown in the UI; leave empty to use build/git metadata

Project Structure

├── main.go              # Entry point, routing, middleware
├── whois.go             # WHOIS/RDAP query logic
├── handlers.go          # HTTP handlers (search, whois API)
├── ratelimit.go         # IP-based rate limiter
├── config.go            # Configuration and environment loading
├── config/
│   ├── whois-servers.json
│   ├── default-tlds.json
│   └── registrar-prices.json
├── scripts/
│   └── update-registrar-prices.mjs
├── static/
│   ├── index.html       # Main page
│   ├── app.js           # Frontend logic + i18n
│   ├── style.css
│   ├── lang/            # Translation files
│   │   ├── en.json
│   │   ├── zh.json
│   │   ├── ja.json
│   │   ├── ko.json
│   │   └── es.json
│   ├── tos.html
│   ├── privacy.html
│   └── legal.css
└── deploy/              # systemd, env-file & nginx configs

Deployment

1. VPS prerequisites

  • Ubuntu 20.04+ with Nginx installed
  • Domain DNS A record pointing to VPS IP
  • (Optional) Redis for caching

2. First-time server setup

# Create application directory and server-side env file
sudo mkdir -p /opt/dmcheck
sudo chown deployer:deployer /opt/dmcheck
sudo install -m 0644 deploy/dmcheck.env.example /opt/dmcheck/dmcheck.env
sudo chown deployer:deployer /opt/dmcheck/dmcheck.env

# Build locally and upload (or let GitHub Actions do it)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o dmcheck .
scp dmcheck your-user@your-vps:/opt/dmcheck/

# Install systemd service
sudo cp deploy/dmcheck.service /etc/systemd/system/dmcheck.service
sudo systemctl daemon-reload
sudo systemctl enable dmcheck
sudo systemctl start dmcheck

3. Nginx + HTTPS

# Copy HTTP-only config and enable
sudo cp deploy/dmcheck.nginx /etc/nginx/sites-available/dmcheck
sudo ln -s /etc/nginx/sites-available/dmcheck /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

# Issue certificate — certbot auto-adds SSL config to Nginx
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d dmcheck.app

Certbot will modify the Nginx config to add SSL listeners, redirect HTTP→HTTPS, and set up auto-renewal.

5. GitHub Actions auto-deploy

The workflow in .github/workflows/deploy.yml automatically builds and deploys on every push to publish.

Add these secrets in your GitHub repo (Settings → Secrets → Actions):

SecretValue
VPS_HOSTYour VPS IP or hostname
VPS_PORTSSH port (if not 22)
VPS_USERdeployer
VPS_SSH_KEYPrivate key for SSH access

Flow: git push origin publish → GitHub Actions builds linux/amd64 binary → scp to VPS → systemctl restart dmcheck. The workflow deploys the binary only; it does not upload or overwrite /opt/dmcheck/dmcheck.env.

6. Environment variables on VPS

Edit the environment file loaded by systemd. This is a persistent server-side file, not a committed repo file:

sudo nano /opt/dmcheck/dmcheck.env
RATE_LIMIT=5
RATE_BURST=10
REGISTERED_CACHE_TTL=2160h
REGISTRAR_PRICES_ENABLED=true
GA_ID=G-XXXXXXXXXX
SITE_URL=https://dmcheck.app
# Leave empty to use the build/git-derived version.
APP_VERSION=

Then run:

sudo systemctl daemon-reload
sudo systemctl restart dmcheck

Acknowledgments

Contributing

See CONTRIBUTING.md for guidelines on adding translations, WHOIS servers, and submitting pull requests.

License

AGPL-3.0