A Go web application that queries the RDAP protocol (RFC 7483/7484) to retrieve domain name registration information, with automatic WHOIS fallback (RFC 3912) when the TLD is not covered.
1. Load IANA bootstrap (dns.json, RFC 7484)
→ find the exact RDAP server for each TLD
2. Direct RDAP query + registrar referral link follow
3. If TLD is missing from bootstrap → WHOIS fallback (TCP port 43)
→ WHOIS referral follow ("WHOIS Server:" field)
go run .
# or
go build -o rdap-lookup && ./rdap-lookupThen open http://localhost:8080
Configuration is done via a YAML file and/or environment variables, using the
shared open-go-base/appconf package (built on
cleanenv).
By default, the config.yaml file is read if it exists; otherwise only
environment variables are used.
# Specify an alternative configuration file
./rdap-lookup -c /path/to/config.yamlserver:
host: "127.0.0.1"
port: 8080
rate_limit: 100
# trusted_proxies: ["127.0.0.1/32"] # required behind a reverse proxy
cors:
origin: ""
logging:
level: "error" # default; set "debug" to enable per-request tracing
format: "text" # "text" or "json"
source: false # include source file:line in logsThe server and logging settings come from the shared open-go-base appconf.Web
and appconf.Logging fragments (the OGR_ prefix is applied at composition).
| Variable | Description | Default |
|---|---|---|
OGR_HOST |
Server listen IP | 127.0.0.1 |
OGR_PORT |
Server listen port | 8080 |
OGR_RATE_LIMIT |
Number of requests allowed per minute per IP | 100 |
OGR_TRUSTED_PROXIES |
Trusted proxy CIDRs (comma-separated) | |
OGR_CORS_ORIGIN |
Allowed CORS origin (empty = disabled) | |
OGR_LOG_LEVEL |
Log level (debug, info, warn, error) |
error |
OGR_LOG_FORMAT |
Log output format (text or json) |
text |
OGR_LOG_SOURCE |
Include source file:line in logs |
false |
Example:
OGR_PORT=9090 OGR_RATE_LIMIT=30 ./rdap-lookup
# Bind all interfaces (default is loopback 127.0.0.1)
OGR_HOST=0.0.0.0 ./rdap-lookupThe default listen host is 127.0.0.1, so the service is meant to run behind a
reverse proxy (see docs/examples/). X-Forwarded-For / X-Real-IP are only
trusted when the connection originates from an address listed in
OGR_TRUSTED_PROXIES. Set it to your proxy's address, otherwise every request
appears to come from the proxy and all clients share a single rate-limit bucket:
# nginx on the same host forwarding X-Forwarded-For / X-Real-IP
OGR_TRUSTED_PROXIES=127.0.0.1/32 ./rdap-lookup- Listen host now defaults to
127.0.0.1(previously all interfaces). SetOGR_HOST=0.0.0.0to restore binding on every interface. - Logging config moved from the top-level
log_level:key to a nestedlogging:block (level/format/source). An oldlog_level:entry in a YAML file is silently ignored — migrate it tologging.level:. TheOGR_LOG_LEVELenvironment variable is unchanged. The default level is nowerror(wasinfo). - Default rate limit changed from
12to100req/min per IP. SetOGR_RATE_LIMITto keep a stricter value.
Logs are emitted as structured records via log/slog, configured by the shared
open-go-base/logx logger.
OGR_LOG_LEVELcontrols verbosity. Set it todebugto see per-request tracing (RDAP server selection, referrals, bootstrap loads). At the defaulterrorlevel only errors are logged.OGR_LOG_FORMAT=jsonswitches from the human-readable text handler to JSON, suitable for log shippers.OGR_LOG_SOURCE=trueadds the sourcefile:line.- The startup banner (listen address, routes, settings) is always written to stderr, independently of the log level.
# Verbose JSON logs for debugging
OGR_LOG_LEVEL=debug OGR_LOG_FORMAT=json ./rdap-lookup{"time":"2026-06-03T00:28:08Z","level":"DEBUG","msg":"rdap: response received","url":"https://rdap.verisign.com/com/v1/domain/example.com"}| URL | Description |
|---|---|
GET / |
Web interface |
GET /lookup?domain=example.com |
HTML result |
GET /api/lookup?domain=example.com |
JSON result |
GET /api/status |
IANA bootstrap status |
The UI and error messages are available in 7 languages (English, French, German,
Italian, Spanish, Chinese, Russian), powered by the shared open-go-base/i18n library.
Translations live in locales/*.yaml and are embedded into the binary (no files to
deploy alongside it).
The language is negotiated per request: the optional ?lang= query parameter wins,
otherwise the Accept-Language header is matched, falling back to English.
# Force French regardless of the browser's Accept-Language
curl 'http://localhost:8080/?lang=fr'.
├── main.go # HTTP server, routing, embedded assets
├── templates/
│ ├── index.html # Search page
│ ├── about.html # About page
│ └── result.html # Results page (RDAP + WHOIS)
├── locales/ # Embedded UI translations (open-go-base/i18n, one YAML per language)
├── static/ # Static assets (CSS, JS, fonts)
└── internal/
├── config/config.go # Configuration struct (open-go-base/appconf fragments)
├── domain/domain.go # Domain utilities (ExtractTLD)
├── model/model.go # Shared types + API DTO
├── bootstrap/bootstrap.go # IANA Bootstrap RFC 7484 (24h cache)
├── handler/
│ ├── handler.go # App, rate limiter, middleware, lookup
│ ├── api.go # JSON API handler
│ ├── index.go # Home page handler
│ ├── lookup.go # HTML result handler
│ └── status.go # Bootstrap status handler
├── rdap/rdap.go # RDAP client + vCard/jCard parsing
├── status/status.go # EPP status descriptions
└── whois/whois.go # WHOIS TCP port 43 client + parsing
Via RDAP:
- EPP statuses (with RFC 7483 tooltips)
- Dates (registration, expiration, last modified)
- Registrar (name, IANA ID, email, phone)
- Registrant / Admin / Tech (vCard/jCard, RFC 6350)
- Nameservers
- DNSSEC (delegation signed, DS records)
- RDAP server used + registrar referral followed
- Full raw JSON
Via WHOIS (fallback):
- Dates, statuses, nameservers
- Registrar + contacts (depending on registry availability)
- Basic DNSSEC
- Full raw text
- Clear indication that RDAP was not available
go test ./...- RFC 7483 — RDAP Object Classes
- RFC 7484 — Finding the RDAP Service (IANA Bootstrap)
- RFC 7095 — jCard
- RFC 3912 — WHOIS Protocol
- client.rdap.org (inspiration)
- Simon Leblanc contact@leblanc-simon.eu
- Claude.ai
- Junie
