🔮 Seer
Domain intelligence at your fingertips.
A high-performance, multi-interface domain utility suite — query WHOIS, RDAP, DNS, SSL, and more from the terminal, Python, REST API, or AI assistants.
Features · Quick Start · CLI Usage · Python · REST API · MCP Server
✨ Features
🔍 Lookups & Discovery
- Smart Lookup — concurrent RDAP + WHOIS with fallback
- WHOIS — registrant, registrar, and expiration data
- RDAP — modern protocol for domains, IPs, and ASNs
- Domain Info — merged RDAP + WHOIS in a flat structure
- Reverse DNS — PTR lookups for IP addresses
- TLD Info — WHOIS server, RDAP endpoint, and registry
- Subdomain Enumeration — CT logs, with live/dead + takeover-risk classification and baseline diffing
- Domain Availability — check if a domain is registered
🌐 DNS & Propagation
- DNS Resolution — dig-style queries over 20 record types: status, CNAME chain, wildcard detection
- DNS Trace — follow the delegation down from the root servers (
+trace) - Encrypted DNS — DoT (
tls://) and DoH (https://) transports - DNS Propagation — 20 public resolvers across North America, Europe and Asia Pacific
- DNS Monitoring — track record changes over time
- DNS Comparison — compare records across two nameservers
- DNSSEC Validation — check DNSSEC configuration
- NS Delegation Check — parent delegation vs. zone NS, with lame-server probing
🛡️ Security & Health
- Domain Status — HTTP status, title, SSL, and expiration
- SSL Chain Inspection — full chain, SANs, key details, validity
- CAA Policy — who may issue certs, vs. actual issuer
- Email Posture — SPF, DMARC, MTA-STS, BIMI, and DANE checks
- Look-alike Detection — confusable/homoglyph domains
- Registration Drift — diff live registration vs. a baseline
- Domain Watchlist — monitor expiring certs and registrations, with optional webhook alerts
- SSRF Protection — blocks requests to private/reserved IPs
⚡ Power Features
- Bulk Operations — process domain lists with CSV export
- Domain Diff — side-by-side comparison of two domains
- Field Extraction —
--fields(implies--quiet) for scriptable output - 4 Output Formats — human, JSON, YAML, markdown
- Interactive REPL — with tab completion and history
- Environment Doctor — one-shot config/DNS/WHOIS/RDAP diagnosis
- Semantic Exit Codes — for CI/CD scripting
- Shell Completions — bash, zsh, fish, PowerShell (plus man pages)
🏗️ Four Interfaces, One Core
┌──────────────────────────────────────────────────────────────────┐
│ User Interfaces │
├─────────────┬─────────────┬──────────────┬───────────────────────┤
│ seer-cli │ seer-py │ seer-api │ seer-api │
│ Terminal │ Python │ REST API │ MCP Server │
└──────┬──────┴──────┬──────┴──────┬───────┴──────────┬────────────┘
│ │ │ │
│ └─────────────┼──────────────────┘
│ │
▼ ▼
┌──────────────────────────────────────────────────────────────────┐
│ seer-core │
│ Core Rust Library │
├──────────┬────────┬──────┬────────┬──────┬──────┬────────────────┤
│ WHOIS │ RDAP │ DNS │ Status │ SSL │ Bulk │ Diff/Watch │
└──────────┴────────┴──────┴────────┴──────┴──────┴────────────────┘
🚀 Quick Start
Install the CLI
Homebrew (macOS/Linux — prebuilt binary, no Rust toolchain required):
Cargo (builds from source):
Prebuilt binaries and shell/PowerShell installers for each release are also on the Releases page.
Run your first lookup
Using Seer from Rust code instead? See Rust Library.
Requirements: Rust 1.89+ · Python 3.10+ (for Python bindings/API)
📦 Packages
| Package | Type | Install | Description |
|---|---|---|---|
| seer-core | Rust library | cargo add seer-core |
Core library — all business logic |
| seer-cli | Rust binary | brew install TheZacillac/tap/seer or cargo install seer-cli |
The seer command-line tool |
| seer-py | Python extension | pip install ./seer-py |
Python bindings via PyO3 (import seer); PyPI (domain-seer) planned |
| seer-api | Python package | pip install -e . |
REST API + MCP server |
| seer-cli | seer-core | |
|---|---|---|
| What | Executable binary | Rust library crate |
| Install | brew install TheZacillac/tap/seer or cargo install seer-cli |
cargo add seer-core |
| Use | Run seer in your terminal |
use seer_core::* in Rust code |
| Provides | Commands, REPL, formatted output | Structs, clients, async APIs |
| Depends on | seer-core internally | Nothing — it's the foundation |
💻 CLI Usage
Interactive TUI
Launch the full-screen, keyboard-first terminal UI (Catppuccin Frappé, with a Latte light theme):
The lens sidebar covers every Seer capability, grouped, each pulling live data from the same seer-core engine as the CLI with full in-pane controls:
- LOOKUP — Overview · WHOIS · RDAP (Domain · IP · ASN) · Reverse DNS · Availability · TLD Info
- DNS — Records (+ custom nameserver) · DNSSEC · Compare (two resolvers) · Trace (from the root servers) · Propagation · Follow (live monitor)
- SECURITY — SSL / Cert · Status · Subdomains · HTTP Headers · Takeover
- POWER — Diff · Bulk (streaming + CSV export) · Watchlist · History
Keys: j/k move · 1–9 jump to a lens · Tab focus nav⇄pane · [ ] sub-tabs · r raw output (json/yaml/markdown; PgUp/PgDn scroll) · y copy · / look up a domain · : command · ? help · :q quit. In-pane: switchers (TLD, nameserver, Compare resolvers, Bulk op) and editable fields (Diff's 2nd domain, Follow interval/count, Bulk file path).
Subdomains and Takeover query CT logs and probe hosts, so they run only when you press ↵ on them (or use their : command), never just by moving onto them. Removing a watched domain (d) and clearing history (c) ask for a second press.
Commands (:): lookup, whois, rdap <domain|ip|AS####>, dig [@server] <name> [type] [+trace], ssl, status, headers, takeover, reverse <ip>, tld <.tld>, compare <domain> <nsA> <nsB>, diff <a> <b>, set output <human|json|yaml|markdown>, theme <frappe|latte>, copy, q.
The startup theme comes from [tui] theme in ~/.seer/config.toml ("frappe" — the default — or "latte"; unknown names fall back to Frappé). Switch live with :theme latte.
Command Mode
# Smart lookup (concurrent RDAP + WHOIS)
# Comprehensive domain info (merged RDAP + WHOIS)
# WHOIS / RDAP
# DNS queries (dig-style: arguments in any order)
# DNS propagation & monitoring
# DNSSEC & DNS comparison
# Domain health & SSL
# Security intel
# Reverse DNS
# Discovery
# Domain diff
# Watchlist
# Lookup history
# Bulk operations (with CSV export) — ops: lookup, whois, rdap, dig, prop,
# status, avail, info, ssl, caa, posture, confusables
|
# Environment diagnosis (config, DNS, WHOIS port 43, RDAP bootstrap HTTPS)
# Scriptable field extraction
# Shell completions & man pages
seer watch --webhook (or webhook_url under [watch] in ~/.seer/config.toml — the flag wins) POSTs the check-all report as JSON. Delivery is best-effort: a failed POST prints a stderr warning and never changes the exit code, and the URL passes the same SSRF guard as every other outbound request.
DNS Queries
seer dig reports a query the way dig does: the response status, the header
flags, and the answer with any CNAME chain first, every record under its real
owner name. Like dig, it sends one query per record type straight to the
nameserver (the default upstream, Google Public DNS, unless @server/-s
names one) and shows the response exactly as the server sent it, header
flags included; the servers are asked in order, IPv4 first, over UDP and
again over TCP when the reply is truncated, and one that does not respond
passes the query to the next, within two DNS timeouts.
$ seer dig www.github.com
DNS A Records: www.github.com
─────────────────────────────
status: NOERROR flags: qr rd ra server: default time: 23 ms
Answer:
www.github.com 2944 CNAME github.com.
github.com 60 A 140.82.114.3
Note: DNS responses are not DNSSEC-validated
- NXDOMAIN vs NODATA. "Name does not exist (NXDOMAIN)" and "No AAAA
records (NODATA — the name exists)" are told apart, each shown with the
zone's SOA when the server sends it. Behind a CNAME chain the chain is
shown and the verdict is about its last target — "The CNAME target
gone.example.net. does not exist (NXDOMAIN)" for a dangling CNAME — or,
from a server that does not recurse, says it returned the CNAME without
following it. SERVFAIL, REFUSED and other response codes (EDNS extended
ones such as BADVERS included) are reported as the status, not as errors.
A referral — an
@serverthat serves only a parent zone pointing at the child zone's nameservers, with theaaflag clear — reads "No answer: referral to ", which says nothing about whether the name exists. - Special-use names.
localhost,127.in-addr.arpa(-x 127.0.0.1),invalid,onionand the other RFC 6761 names are answered by seer's resolver itself and never sent to a server; the result says so (server: none, no flags and a note;answered_locally: truein JSON). - Wildcards. For a name below its registrable domain (
www.example.com, notexample.com), a random sibling (seer-probe-….example.com) is queried alongside (never for a special-use name). When it resolves too, a note says a wildcard answers there and whether this answer matches it (likely wildcard-synthesized) or differs. - Record types.
HTTPSandSVCB(priority, target and parameters such asalpn,ipv4hintandech) and the DNSSEC key-rollover recordsCDSandCDNSKEYare supported — see DNS Record Types.ANYqueries A, AAAA, CNAME, MX, NS, TXT, SOA, CAA, HTTPS, DS and DNSKEY concurrently and merges the answers, since servers may answer a realANYquery minimally (RFC 8482). +short/--shortprints only the values, one per line (CNAME targets first), and nothing when there is no answer. It ignores--formatand can't be combined with-q/--fields. A+trace +shortthat stopped early prints why on stderr and exits1.+trace/--traceasks one server of each zone directly, from the root servers down, and shows one hop per delegation level: the server and address, the status, the referral or the answer, and any servers that failed. A server that fails or gives a lame (empty, non-authoritative) reply is passed over for the zone's next one, and each server is asked over IPv4 first, then over IPv6 when this host has no IPv4 route. The whole walk gets six DNS timeouts (30s by default) and otherwise stops with the hops so far and an error. It takes one record type and no nameserver, and reports a CNAME answer without following it (an NXDOMAIN beside a CNAME, which the zone's server followed itself, names the target that does not exist). Every server address is checked against private and reserved ranges before it is queried.- Scripting.
--format json/yamlprints the result object (name,record_type,server,answered_locally,status,flags,answers,authority,wildcard,query_time_ms), or an array of them for several types, and--quiet --fieldspicks values from it. When some of several types fail, the rest still print, each failure is reported on stderr, and the command exits1.
The REPL's dig takes the same arguments. seer reverse <ip> is unchanged:
it returns just the PTR records (a JSON list), where seer dig -x <ip>
returns the full result.
Output Formats
Exit Codes
Every command follows the same contract, so $? is safe to gate cron jobs and CI on:
| Code | Meaning |
|---|---|
0 |
Success (and, for check commands, the check passed) |
1 |
Operational error (network, parse, invalid input) — or a check command's negative result |
2 |
Usage error (bad flags or arguments) |
Check commands exit 1 on a negative result even when the command itself ran fine:
| Command | Exits 1 when… |
|---|---|
seer status |
HTTP status is missing or non-2xx, the SSL cert is invalid or expires within 30 days, or the domain expires within 30 days |
seer avail |
The domain is not available (already registered) |
seer dnssec |
The zone's status is anything other than signed (i.e. unsigned, partial, or misconfigured) |
seer compare |
The two nameservers give different answers (response code, CNAME chain or record set) |
seer delegation |
The parent's delegation NS set and the zone's own NS RRset are out of sync, or any delegated server answers lamely |
seer takeover |
Any host is vulnerable or potential (a host that could not be checked is inconclusive and does not fail the scan) |
seer doctor |
Any check reports FAIL. WARN (degraded but usable, e.g. a malformed config file running on defaults) still exits 0 |
seer drift |
Material drift is found vs. the stored baseline (a first run with no baseline exits 0) |
seer subdomains --diff |
New names appeared vs. the stored baseline (removals and a missing baseline exit 0) |
seer watch |
Issues at or above the --fail-on threshold: critical (default) fails only on critical issues, warning fails on warnings too. add/remove/list exit 0 on success |
seer bulk |
Every domain in the batch failed — partial failures exit 0, since per-row status is already in the CSV/JSON output |
seer dig is not a check command: as with dig, any answer the server gave
exits 0 — NXDOMAIN, NODATA, SERVFAIL and REFUSED included, and a +trace
that stopped early reports where. It exits 1 on invalid arguments, a
timeout or other failure to get an answer, when any of several record
types failed, or when a +trace +short stopped early (its values alone
could not say so).
All other commands exit 0 on success and 1 on error. With --format json, yaml, or markdown, errors are written to stderr in that format (e.g. {"error": "..."}) instead of colored prose.
Interactive REPL
Launch by running seer with no arguments:
$ seer
seer> lookup example.com
seer> dig github.com MX
seer> dig @1.1.1.1 www.github.com A AAAA +short
seer> status cloudflare.com
seer> delegation example.com
seer> doctor
seer> set output json
seer> help
seer> exit
Features: command history (~/.seer_history), tab completion, loading spinners, persistent session state.
🐍 Python Library
# Smart lookup
=
# WHOIS / RDAP
=
= # Auto-routes IP / ASN / domain (alias: rdap_auto)
=
=
=
# DNS
=
= # Also: status, flags, authority, wildcard
= # Hops from the root servers down (dig +trace)
=
= # Parent vs. zone NS + lameness
=
# Stop a running dns_follow from another thread
= # The 20 supported record types
# Domain health & SSL
=
=
=
=
# Security intel
= # SPF / DMARC / MTA-STS
= # HTTP security headers, graded A+-F
= # Subdomain takeover scan
= # Look-alike domains
# Availability & info
=
=
= # WHOIS server, RDAP endpoint, registry
= # Every TLD seer knows about
# Comparison & enumeration
=
=
=
= # live/dead + takeover risk
# Bulk operations
=
=
=
=
=
=
=
=
# bulk_* calls return one dict per domain:
# {"operation": {"type": ..., "domain": ...}, "success": bool,
# "data": ... or None, "error": str or None, "duration_ms": int}
# Every bulk_* call takes an optional keyword-only progress callback
=
# SSRF helpers (used by seer-api before any user-supplied connect target)
# ValueError on reserved/private addresses
# ("1.1.1.1", 853); None if the spec is invalid
# Errors: ValueError (invalid input), TimeoutError, ConnectionError (WHOIS
# connect), or a seer.SeerError subclass (a RuntimeError): RateLimitedError,
# WhoisServerNotFoundError, DnsError, UpstreamError, LookupFailedError,
# ParseError, TlsError, ConfigError
pass
=
🦀 Rust Library
[]
= "0.52"
= { = "1", = ["full"] }
use ;
async
See docs.rs/seer-core for the full API reference.
🌍 REST API
Each lookup is a GET endpoint (/lookup/{domain}, /dns/{domain}/{record_type},
/dns/trace/{domain}, /ssl/{domain}, /takeover/{domain}, …). lookup,
whois, dns, propagation, status, ssl, availability, and info
also accept a POST …/bulk domain list, and all but availability and
info can stream bulk results as Server-Sent Events from …/bulk/stream.
The full endpoint table is in
seer-api/README.md.
Secure by default: the server binds loopback only, refuses a non-loopback
SEER_HOST without SEER_API_KEY, serves /docs only when
SEER_DOCS_ENABLED=true, and refuses multi-worker runs on the in-memory
rate-limit store. See
Deployment defaults.
# Examples
API docs (when SEER_DOCS_ENABLED=true):
Swagger UI · ReDoc
🤖 MCP Server
Integrate Seer with AI assistants via the Model Context Protocol. Two transports expose the same tools:
# Local subprocess transport (Claude Desktop, etc.)
# Remote / web transport (mounted on the REST API)
The Streamable HTTP endpoint runs stateless inside the existing seer-api
process and inherits SEER_API_KEY auth, request logging, and the body-size
cap. See seer-api/README.md
for the wire format.
Generate a random bearer token for the connector:
# Capture into a shell var
KEY=
# Or eval directly into the current shell
SEER_API_KEY= SEER_HOST=0.0.0.0
32 tools: one per lookup (seer_lookup, seer_dig, seer_dns_trace,
seer_ssl, seer_takeover, …) plus bulk variants (seer_bulk_status, …).
The full tool list and the Claude Desktop configuration are in
seer-api/README.md.
📡 DNS Record Types
| Type | Description | Type | Description | |
|---|---|---|---|---|
A |
IPv4 address | PTR |
Pointer record | |
AAAA |
IPv6 address | SRV |
Service locator | |
MX |
Mail exchange | NAPTR |
Naming authority pointer | |
TXT |
Text records | DNSKEY |
DNSSEC public key | |
NS |
Nameserver | DS |
Delegation signer | |
SOA |
Start of authority | CDS |
Child DS (DNSSEC key rollover) | |
CNAME |
Canonical name | CDNSKEY |
Child DNSKEY (DNSSEC key rollover) | |
CAA |
CA authorization | SSHFP |
SSH key fingerprint | |
HTTPS |
HTTPS service binding (ALPN, address hints, ECH) | TLSA |
DANE TLS association | |
SVCB |
General service binding | ANY |
A, AAAA, CNAME, MX, NS, TXT, SOA, CAA, HTTPS, DS and DNSKEY, queried concurrently |
🌏 Global DNS Propagation Servers
Propagation checks query 30 nameservers across 6 regions:
| Region | Servers |
|---|---|
| 🇺🇸 North America | Google 8.8.8.8 · Cloudflare 1.1.1.1 · OpenDNS · Quad9 · Level3 |
| 🇪🇺 Europe | DNS.Watch · Mullvad · dns0.eu · Yandex · UncensoredDNS |
| 🌏 Asia Pacific | AliDNS · 114DNS · Tencent DNSPod · TWNIC · HiNet |
| 🌎 Latin America | Claro Brasil · Telefonica Brasil · Antel Uruguay · Telmex · CenturyLink |
| 🌍 Africa | Liquid Telecom · SEACOM · Safaricom · MTN South Africa · Telecom Egypt |
| 🌐 Middle East | Etisalat UAE · STC Saudi · Bezeq Israel · Turk Telekom · Ooredoo Qatar |
⚙️ Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
RUST_LOG |
Logging level (trace / debug / info / warn / error) |
— |
SEER_LOG_LEVEL |
API log level; ARCANUM_LOG_LEVEL takes precedence when set |
INFO |
SEER_DOMAIN_ALLOWLIST |
Comma-separated allowlist restricting which domains may be queried | — |
SEER_HOST |
API bind host. Non-loopback requires SEER_API_KEY (without it, requests arriving on a non-loopback interface get a 503 however the server was started) |
127.0.0.1 |
SEER_PORT |
API bind port | 8000 |
SEER_API_KEY |
Bearer token required for all non-/health requests |
— |
SEER_CORS_ORIGINS |
Comma-separated CORS origins for the REST API. Unset: no CORS headers, and without SEER_API_KEY a request from a non-loopback Origin is refused (403) |
— |
SEER_DOCS_ENABLED |
Expose /docs, /redoc, /openapi.json |
false |
SEER_METRICS_ENABLED |
Expose /metrics to non-loopback clients |
false |
SEER_RATE_LIMIT |
Per-client limit for the MCP endpoint (POST /mcp) as <count>/<period>; ;-separated limits are all enforced. REST routes keep their own fixed limits |
30/minute |
SEER_RATE_LIMIT_STORAGE |
Rate-limit storage URI (e.g. redis://host:6379) |
memory:// |
SEER_REQUEST_TIMEOUT |
Per-request deadline (seconds) covering the SSRF pre-check, the core call and bulk SSE streams (slot wait included); on expiry the client gets a 504 (a stream: an error event). 0 disables |
0 |
SEER_DISPATCH_THREADS |
Max threads in the pool running blocking core calls (REST + /mcp) |
50 |
SEER_MAX_CONCURRENT_STREAMS |
Max in-flight bulk SSE stream jobs per worker process | 8 |
SEER_TRUST_PROXY |
Trust X-Forwarded-For from SEER_TRUSTED_PROXY_IPS |
false |
SEER_TRUSTED_PROXY_IPS |
Comma-separated IPs allowed to set X-Forwarded-For |
— |
SEER_MCP_ALLOWED_HOSTS |
Comma-separated Host: values for the MCP /mcp endpoint — setting this turns on DNS-rebinding protection |
— |
SEER_MCP_ALLOWED_ORIGINS |
Comma-separated Origin: values for the MCP /mcp endpoint (browser hosts) |
— |
WEB_CONCURRENCY |
Uvicorn worker count. >1 requires a non-memory:// store |
1 |
UVICORN_WORKERS |
Fallback worker count when WEB_CONCURRENCY is unset (same >1 store rule) |
1 |
SEER_RELOAD |
Uvicorn auto-reload for the seer-api entry point (dev only) |
false |
Config File
Initialize a config file at ~/.seer/config.toml:
Settings: default output format, nameserver (UDP, tls://, or https://),
per-protocol timeouts, bulk concurrency, rate-limit delay — all clamped to
safe ranges — plus a [watch] table (webhook_url for seer watch report
delivery) and a [tui] table (theme = "frappe" or "latte"). Applies to
the CLI and REPL; the TUI reads the output format and theme.
Timeouts
| Client | Default |
|---|---|
| WHOIS | 15s |
| RDAP | 15s |
| DNS | 5s (2 retries) |
| HTTP / SSL | 10s |
| CT logs (subdomains) | 30s per request (ct_secs); each source at most 3 of them |
| Propagation | 15s |
Bulk Operations
- Input: plain text (one domain per line) or CSV (first column)
- Comments: lines starting with
#are skipped - Concurrency: default 10, max 50
- API limit: max 100 domains per request
🔮 Roadmap
- Scheduled Monitoring Daemon —
seer monitor --config monitors.tomlas a background service with notifications (email, Slack, PagerDuty) for expiring domains, SSL certificates, and unexpected DNS changes. The natural evolution ofwatch,follow, andstatusinto persistent monitoring.
🛠️ Development
Building
&& &&
Testing
&& RUST_LOG=debug
Linting
Project Structure
The four packages are listed under Packages; all business
logic lives in seer-core, and the other three are thin interfaces over it.
The annotated per-module map is in
CLAUDE.md → Codebase Structure.
🔧 Technology Stack
| Layer | Technologies |
|---|---|
| Core | Rust · Tokio · Reqwest (rustls) · Hickory-resolver (DNSSEC) · Serde · OpenTelemetry |
| CLI | Clap v4 · Rustyline · Indicatif · Colored · Crossterm |
| Python | PyO3 (ABI3) · FastAPI · Pydantic · MCP |
| Data | WHOIS servers from WooMai/whois-servers · IANA RDAP bootstrap |
📄 License
MIT License — Copyright (c) 2026 Zac Roach
See LICENSE for the full text.