2026-10-18 18:00:40 +02:00
2026-10-18 16:46:45 +02:00
2026-10-18 16:46:45 +02:00
2026-04-15 19:02:39 +02:00
2026-10-18 18:00:40 +02:00
2026-10-18 18:00:40 +02:00
2026-04-09 18:07:09 +02:00
2026-04-09 18:07:09 +02:00
2026-04-09 18:07:09 +02:00
2026-10-18 16:46:45 +02:00
2026-10-18 16:46:45 +02:00
2026-08-10 19:43:14 +02:00
2026-08-11 00:53:44 +02:00
2026-10-18 18:00:40 +02:00

who dis?

A lightweight, self-hosted RDAP and WHOIS lookup tool with a clean web interface and an MCP server. Query domain names, TLDs, IP addresses, CIDR ranges, ASNs, and tagged entity handles from your browser or your AI assistant.

The default Auto mode tries RDAP first and falls back to WHOIS when RDAP is unavailable. RDAP responses are rendered as complete, syntax-highlighted JSON on the server, with the exact upstream body available in a collapsed raw view; the application does not require browser JavaScript.

Screenshot of who dis?

Deploying

docker run -p 8080:8080 gitfield.org/fsky/whodis:latest

A Quadlet unit file is available at contrib/quadlet/whodis.container for deploying with Podman and systemd.

From source

Requires Go 1.26+.

go build ./cmd/whodis
./whodis

Then open http://localhost:8080 in your browser.

MCP server

whodis serves a Model Context Protocol endpoint at /mcp on the same port as the web UI, so assistants such as claude.ai, ChatGPT, and Claude Code can run lookups without network access in their own sandbox. It uses stateless Streamable HTTP and needs a public HTTPS URL for hosted assistants; terminate TLS at your reverse proxy.

The server provides one read-only tool, lookup, with the same Auto, RDAP, and WHOIS modes as the web UI. Results are compact JSON with one entry per queried server and a status of ok, not_found (an authoritative RDAP 404), partial (a later referral failed after an earlier server answered), or failed. To keep the assistant's context small, RDAP documents omit their notices, links, and rdapConformance members unless the tool is called with raw: true, each body is capped at 32 KiB, and a body repeated by a later referral is only included once. Both front ends share one cache.

Access tokens

Set MCP_TOKENS to one or more comma-separated tokens of at least 32 characters, for example generated with openssl rand -hex 32. A client presents a token either in an Authorization: Bearer <token> header or, for assistants that can only be given a URL (claude.ai, ChatGPT), as a path segment: https://whodis.example/mcp/<token>. Requests with a valid token are never rate limited. With MCP_REQUIRE_TOKEN=true, requests without one are rejected, making the endpoint private.

A token in the URL is recorded by anything that logs request paths, such as your reverse proxy's access log. Rotate a leaked token by replacing it in MCP_TOKENS.

Hosted assistants call the server from their provider's shared IP ranges, so per-IP limits would make their users throttle each other. MCP rate limiting is therefore off by default.

Go packages

The protocol clients are importable without pulling the web application into your program:

import "gitfield.org/fsky/whodis/whois"

client := whois.NewClient()
result, err := client.Lookup(ctx, "example.com")

RDAP exposes the same raw-response style of API:

import "gitfield.org/fsky/whodis/rdap"

client := rdap.NewClient()
result, err := client.Lookup(ctx, "example.com")
for _, response := range result.Responses {
    fmt.Printf("%s returned %d bytes\n", response.URL, len(response.Body))
}
response, err := client.Query(ctx, "https://rdap.example/rdap/domain/example.com")

The repository remains one Go module, so its downloaded source archive also contains the web app, but importing whois or rdap neither compiles nor initializes the application package.

Lookup chooses an appropriate server and follows recognized referrals. Query sends an exact, single-line query to a caller-selected whois.Endpoint. Responses are returned as raw bytes together with the endpoint, exact wire query, and referral metadata; interpreting registry records is left to the caller.

When a later referral fails, Lookup returns the completed responses together with the error. Errors support errors.Is and errors.As, including typed operation errors and registry web URLs for resources without WHOIS service.

Automatic lookups only contact public WHOIS/RWhois endpoints by default. Direct queries permit caller-selected private endpoints, and both policies can be replaced with client options.

RDAP Lookup discovers the service from the checked-in IANA bootstrap data, tries advertised alternatives, follows safe redirects and top-level registrar referrals, and validates successful responses as JSON. Like WHOIS, it retains completed responses in traversal order when a later referral fails. Query fetches an exact caller-selected HTTP(S) URL. It permits private endpoints by default, while automatic lookups only contact public addresses and pin validated DNS results when dialing.

Some ccTLD registries run RDAP services that are not listed in the IANA bootstrap registry. A short hand-maintained list in rdap/data.go (currently .co, .de, and .io) is consulted only when IANA has no service for a domain, and a test fails once a refreshed snapshot makes an entry redundant.

Both routing snapshots are compiled into the packages. Normal builds and tests perform no bootstrap downloads. Maintainers can refresh the checked-in data from the public registries with:

make refresh-data

Use make refresh-whois or make refresh-rdap to refresh only one snapshot.

Development

The root Makefile provides the common workflows:

make                 # build ./whodis (the default target)
make run             # run from source
make check           # formatting check, go vet, and tests; fully offline
make test-race       # run tests with the race detector
make fmt             # format Go source in place
make container       # build whodis:dev with Docker (overridable)
make help            # list every target

Variables can be overridden on the command line, for example make BINARY=whodis-dev build or make CONTAINER_ENGINE=podman IMAGE=localhost/whodis:dev container.

Configuration

Configuration is handled through environment variables:

Variable Default Description
PORT 8080 TCP port the HTTP server listens on
CACHE_TTL 24h Cache duration for successful lookups. Accepts Go duration strings (1h, 30m, etc.)
RATE_LIMIT_PER_MINUTE 20 Per-IP rate limit applied only to web UI cache misses. Set to 0 to disable.
RATE_LIMIT_BURST 10 Maximum burst of cache-miss lookups a single IP can make before throttling.
TRUST_PROXY_HEADERS false When true, honor X-Forwarded-For / X-Real-IP for client IP and X-Forwarded-Proto for generated absolute URLs. Only enable when whodis is behind a trusted reverse proxy.
ENABLE_WEB_UI true Serve the web UI. Set to false for an MCP-only instance.
ENABLE_MCP true Serve the MCP endpoint at /mcp. Set to false for a web-only instance.
MCP_RATE_LIMIT_PER_MINUTE 0 Per-IP rate limit applied only to MCP cache misses without a valid token. 0 disables it.
MCP_RATE_LIMIT_BURST 20 Maximum burst of MCP cache-miss lookups a single IP can make before throttling.
MCP_TOKENS empty Comma-separated MCP access tokens (at least 32 characters of A-Z a-z 0-9 - . _ ~). Requests with a valid token are never rate limited.
MCP_REQUIRE_TOKEN false When true, reject MCP requests without a valid token.

whodis refuses to start when ENABLE_WEB_UI, ENABLE_MCP, or MCP_REQUIRE_TOKEN is not a valid boolean, when both front ends are disabled, when a token is malformed, or when tokens are required but none are set.

Error results are cached for 5 minutes regardless of CACHE_TTL. Rate limiting only applies to cache misses. Popular lookups served from cache are never throttled.

License

The code of this project is released under the Zero-Clause BSD License. See the LICENSE file for details.

Languages
Go 92.7%
CSS 3.4%
HTML 3.2%
Makefile 0.6%
Dockerfile 0.1%