<div align="center">
# 🔮 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.
[](https://github.com/TheZacillac/seer/actions/workflows/ci.yml)
[](https://crates.io/crates/seer-cli)
[%22&replace=%241&label=homebrew&logo=homebrew&logoColor=white&color=fbb040)](https://github.com/TheZacillac/homebrew-tap)
[](LICENSE)
[](https://www.rust-lang.org)
[](https://www.python.org)
[Features](#-features) · [Quick Start](#-quick-start) · [CLI Usage](#-cli-usage) · [Python](#-python-library) · [REST API](#-rest-api) · [MCP Server](#-mcp-server)
</div>
---
## ✨ Features
<table>
<tr>
<td width="50%">
**🔍 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
</td>
<td width="50%">
**🌐 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
</td>
</tr>
<tr>
<td>
**🛡️ 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
</td>
<td>
**⚡ 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)
</td>
</tr>
</table>
### 🏗️ 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):
```bash
brew install TheZacillac/tap/seer
```
**Cargo** (builds from source):
```bash
cargo install seer-cli
```
Prebuilt binaries and shell/PowerShell installers for each release are also on
the [Releases page](https://github.com/TheZacillac/seer/releases).
### Run your first lookup
```bash
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](#-rust-library).
> **Requirements:** Rust 1.89+ · Python 3.10+ (for Python bindings/API)
---
## 📦 Packages
| **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 |
<details>
<summary><b>seer-cli vs seer-core — which do I need?</b></summary>
| **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 |
</details>
---
## 💻 CLI Usage
### Interactive TUI
Launch the full-screen, keyboard-first terminal UI (Catppuccin Frappé, with a Latte light theme):
```bash
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
```bash
# 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
# 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
```bash
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:
| `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:
| `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
```python
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
```
<details>
<summary><b>Example: Check SSL Certificate</b></summary>
```python
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']}")
```
</details>
---
## 🦀 Rust Library
```toml
[dependencies]
seer-core = "0.49"
tokio = { version = "1", features = ["full"] }
```
```rust
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](https://docs.rs/seer-core) for the full API reference.
---
## 🌍 REST API
```bash
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](seer-api/README.md#endpoints).
**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](seer-api/README.md#deployment-defaults).
```bash
# 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](http://localhost:8000/docs) · [ReDoc](http://localhost:8000/redoc)
---
## 🤖 MCP Server
Integrate Seer with AI assistants via the [Model Context Protocol](https://modelcontextprotocol.io/). Two transports expose the same tools:
```bash
# 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](seer-api/README.md#streamable-http-transport)
for the wire format.
Generate a random bearer token for the connector:
```bash
# 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](seer-api/README.md#available-tools).
---
## 📡 DNS Record Types
| `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**:
| 🇺🇸 **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
| `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`:
```bash
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
| 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
```bash
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
```bash
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
```bash
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](#-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](CLAUDE.md#codebase-structure).
---
## 🔧 Technology Stack
| **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](https://github.com/WooMai/whois-servers) · IANA RDAP bootstrap |
---
## 📄 License
MIT License — Copyright (c) 2026 Zac Roach
See [LICENSE](LICENSE) for the full text.