seer-cli 0.49.0

Interactive CLI for Seer domain name utilities
seer-cli-0.49.0 is not a library.

🔮 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.

CI Crates.io Homebrew License: MIT Rust Python

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 — 16 record types with custom nameservers
  • Encrypted DNS — DoT (tls://) and DoH (https://) transports
  • DNS Propagation — 30 servers across 6 global regions
  • 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 — --quiet --fields 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):

brew install TheZacillac/tap/seer

Cargo (builds from source):

cargo install seer-cli

Prebuilt binaries and shell/PowerShell installers for each release are also on the Releases page.

Run your first lookup

seer lookup example.com        # Smart RDAP + WHOIS lookup
seer dig example.com MX        # DNS query
seer status example.com        # HTTP, SSL, and expiration check
seer ssl example.com           # Full SSL chain inspection
seer                           # Launch interactive REPL
seer tui example.com           # Launch the full-screen TUI

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):

seer tui                  # start on the target prompt
seer tui example.com      # look up a domain on launch

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) · 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) · 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).

Commands (:): lookup, whois, rdap <domain|ip|AS####>, dig, 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)
seer lookup example.com

# Comprehensive domain info (merged RDAP + WHOIS)
seer info example.com

# WHOIS / RDAP
seer whois example.com
seer rdap example.com           # Domain
seer rdap 8.8.8.8               # IP address
seer rdap AS15169               # ASN

# DNS queries
seer dig example.com             # A records (default)
seer dig example.com MX          # Specific record type
seer dig example.com A -s 8.8.8.8                          # Custom nameserver (UDP)
seer dig example.com A -s tls://1.1.1.1                    # DNS over TLS
seer dig example.com A -s https://cloudflare-dns.com/dns-query  # DNS over HTTPS

# DNS propagation & monitoring
seer prop example.com A
seer follow example.com 20 0.5       # 20 checks, 30s interval
seer follow example.com 10 1 MX --changes-only

# DNSSEC & DNS comparison
seer dnssec example.com
seer compare example.com 8.8.8.8 1.1.1.1 MX   # record type trails; defaults to A
seer delegation example.com      # Parent delegation vs. zone NS + lame-server probe

# Domain health & SSL
seer status example.com
seer ssl example.com
seer caa example.com              # CAA policy vs. actual cert issuer
seer posture example.com          # SPF / DMARC / MTA-STS email posture
seer headers example.com          # HTTP security headers + cookie flags, graded A+-F

# Security intel
seer confusables example.com      # Look-alike / homoglyph domains
seer drift example.com --record   # Diff registration vs. baseline, then update it
seer takeover example.com         # Subdomain takeover scan (exit 1 on findings)
seer takeover example.com --host app.example.com   # Check known hosts, skip CT lookup

# Reverse DNS
seer reverse 8.8.8.8

# Discovery
seer avail example.com
seer subdomains example.com
seer subdomains example.com --resolve    # Resolve each name: live/dead + takeover risk
seer subdomains example.com --record     # Store a CT-log baseline
seer subdomains example.com --diff       # Exit 1 when new names appear (cron/CI)
seer tld .com

# Domain diff
seer diff example.com google.com

# Watchlist
seer watch add example.com
seer watch list
seer watch                        # Check all watched domains
seer watch --fail-on warning      # Exit non-zero at warning severity (default: critical)
seer watch --webhook https://hooks.example.com/seer   # Also POST the report as JSON
seer watch remove example.com

# Lookup history
seer history example.com
seer history --clear

# Bulk operations (with CSV export) — ops: lookup, whois, rdap, dig, prop,
# status, avail, info, ssl, caa, posture, confusables
seer bulk lookup domains.txt
seer bulk status domains.txt -o results.csv
seer bulk dig domains.txt MX
seer bulk ssl domains.txt
seer bulk posture domains.txt
cat domains.txt | seer bulk avail -      # Read the list from stdin

# Environment diagnosis (config, DNS, WHOIS port 43, RDAP bootstrap HTTPS)
seer doctor

# Scriptable field extraction
seer --quiet --fields registrar lookup example.com
seer --quiet --fields certificate.issuer status example.com

# Shell completions & man pages
seer completions bash >> ~/.bashrc
seer completions zsh >> ~/.zshrc
seer mangen ./man                 # Write seer.1 + one page per subcommand

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.

Output Formats

seer --format human lookup example.com      # Colored, human-readable (default)
seer --format json lookup example.com       # JSON
seer --format yaml lookup example.com       # YAML
seer --format markdown lookup example.com   # Markdown table

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 return different record sets
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 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

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> 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

import seer

# Smart lookup
result = seer.lookup("example.com")

# WHOIS / RDAP
whois = seer.whois("example.com")
rdap  = seer.rdap("8.8.8.8")                     # Auto-routes IP / ASN / domain (alias: rdap_auto)
rdap  = seer.rdap_domain("example.com")
rdap  = seer.rdap_ip("8.8.8.8")
rdap  = seer.rdap_asn(15169)

# DNS
records     = seer.dig("example.com", record_type="MX", nameserver="tls://1.1.1.1")
propagation = seer.propagation("example.com", record_type="A")
delegation  = seer.delegation("example.com")     # Parent vs. zone NS + lameness
follow      = seer.dns_follow("example.com", "A", iterations=3, interval_minutes=1.0)
seer.cancel_follow()                             # Stop a running dns_follow from another thread
types       = seer.record_types()                # The 16 supported record types

# Domain health & SSL
status = seer.status("example.com")
ssl    = seer.ssl("example.com")
dnssec = seer.dnssec("example.com")
caa    = seer.caa("example.com")

# Security intel
posture     = seer.posture("example.com")        # SPF / DMARC / MTA-STS
headers     = seer.headers("example.com")        # HTTP security headers, graded A+-F
takeover    = seer.takeover("example.com")       # Subdomain takeover scan
confusables = seer.confusables("example.com")    # Look-alike domains

# Availability & info
available = seer.availability("example.com")
info      = seer.info("example.com")
tld       = seer.tld_info(".com")                # WHOIS server, RDAP endpoint, registry
tlds      = seer.all_tlds()                      # Every TLD seer knows about

# Comparison & enumeration
diff       = seer.diff("example.com", "google.com")
comparison = seer.dns_compare("example.com", "A", "8.8.8.8", "1.1.1.1")
subdomains = seer.subdomains("example.com")
classified = seer.subdomains_classify("example.com")  # live/dead + takeover risk

# Bulk operations
results = seer.bulk_lookup(["example.com", "google.com"], concurrency=10)
results = seer.bulk_status(["example.com", "google.com"])
results = seer.bulk_dig(["example.com", "google.com"], record_type="A")
results = seer.bulk_info(["example.com", "google.com"])
results = seer.bulk_ssl(["example.com", "google.com"])
results = seer.bulk_availability(["example.com", "google.com"])
results = seer.bulk_whois(["example.com", "google.com"])
results = seer.bulk_propagation(["example.com"], record_type="A", concurrency=5)

# 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
results = seer.bulk_lookup(domains, progress=lambda done, total, domain: print(f"{done}/{total} {domain}"))

# SSRF helpers (used by seer-api before any user-supplied connect target)
seer.validate_public_host("example.com", 443)   # ValueError on reserved/private addresses
seer.nameserver_target("tls://1.1.1.1")          # ("1.1.1.1", 853); None if the spec is invalid
status = seer.status("example.com")
if cert := status.get("certificate"):
    print(f"SSL Valid: {cert['is_valid']}")
    print(f"Expires:   {cert['valid_until']}")
    print(f"Days left: {cert['days_until_expiry']}")

🦀 Rust Library

[dependencies]
seer-core = "0.49"
tokio = { version = "1", features = ["full"] }
use seer_core::{SmartLookup, DnsResolver, RecordType, StatusClient};

#[tokio::main]
async fn main() -> seer_core::Result<()> {
    // Smart lookup (RDAP → WHOIS fallback)
    let lookup = SmartLookup::new();
    let result = lookup.lookup("example.com").await?;

    // DNS resolution
    let resolver = DnsResolver::new();
    let records = resolver.resolve("example.com", RecordType::MX, None).await?;
    for record in records {
        println!("{}: {}", record.record_type, record.data);
    }

    // Domain status check
    let client = StatusClient::new();
    let status = client.check("example.com").await?;
    println!("HTTP: {:?}", status.http_status);

    Ok(())
}

See docs.rs/seer-core for the full API reference.


🌍 REST API

seer-api   # Starts on http://127.0.0.1:8000 (loopback-only by default)

Each lookup is a GET endpoint (/lookup/{domain}, /dns/{domain}/{record_type}, /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
curl http://localhost:8000/lookup/example.com
curl http://localhost:8000/dns/example.com/MX
curl -X POST http://localhost:8000/lookup/bulk \
  -H "Content-Type: application/json" \
  -d '{"domains": ["example.com", "google.com"]}'

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.)
seer-mcp   # stdio

# Remote / web transport (mounted on the REST API)
seer-api   # exposes POST /mcp using MCP Streamable HTTP

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=$(seer generate-key)

# Or eval directly into the current shell
eval "$(seer generate-key --export)"

SEER_API_KEY=$KEY SEER_HOST=0.0.0.0 seer-api

30 tools: one per lookup (seer_lookup, seer_dig, 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 SSHFP SSH key fingerprint
CNAME Canonical name TLSA DANE TLS association
CAA CA authorization ANY All records

🌏 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 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 REST API *
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) for dispatched core calls; on expiry the client gets a 504. 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:

seer config --init

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
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.toml as a background service with notifications (email, Slack, PagerDuty) for expiring domains, SSL certificates, and unexpected DNS changes. The natural evolution of watch, follow, and status into persistent monitoring.

🛠️ Development

Building

cargo build --release                        # All Rust packages
cd seer-py && maturin develop --release      # Python bindings
cd seer-api && pip install -e .              # REST API + MCP server

Testing

cargo test                    # All Rust tests
cargo test -p seer-core       # Core library only
cd seer-api && pytest         # Python API tests
RUST_LOG=debug cargo test     # With debug logging

Linting

cargo fmt --all -- --check                             # Format check
cargo clippy --workspace --all-targets -- -D warnings  # Lint
cargo deny check                                       # Supply-chain policy (advisories, licenses, sources)

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.