quien-whois-lookup
Installation
SKILL.md
quien — Better WHOIS Lookup Tool
Skill by ara.so — Daily 2026 Skills collection.
quien is a Go-based CLI/TUI tool that replaces whois with tabbed interactive views and JSON-scriptable subcommands for domain WHOIS/RDAP, DNS records, mail configuration (SPF, DMARC, DKIM, BIMI), SSL/TLS, HTTP headers, tech stack detection, and IP/ASN/BGP lookups.
Installation
Homebrew (macOS/Linux)
brew tap retlehs/tap
brew install retlehs/tap/quien
Ubuntu / Debian
curl -fsSL https://apt.quien.dev/install.sh | sudo sh
Go
go install github.com/retlehs/quien@latest
Verify installation
quien --version
Optional — replace system whois:
# Add to ~/.bashrc or ~/.zshrc
alias whois=quien
Core CLI Commands
Interactive TUI (default)
# Launch interactive prompt
quien
# Open TUI directly for a domain
quien example.com
# Open TUI for an IP address
quien 8.8.8.8
JSON output (scriptable)
# Full JSON output for all tabs
quien --json example.com
# Individual subcommands (always output JSON)
quien dns example.com
quien mail example.com
quien tls example.com
quien http example.com
quien stack example.com
quien all example.com # all data in one JSON object
TUI Tab Overview
| Tab | Data shown |
|---|---|
| WHOIS | RDAP-first registration data, registrar, dates, nameservers |
| DNS | A, AAAA, MX, TXT, NS, CNAME, SOA records |
| MX, SPF, DMARC, DKIM, BIMI + VMC chain validation | |
| TLS | Certificate chain, validity, SANs, cipher info |
| HTTP | Response headers, redirects, status codes |
| Stack | WordPress plugins, JS/CSS frameworks, external services |
| IP | Reverse DNS, network info, abuse contacts, ASN via RDAP/BGP |
JSON Subcommand Examples
DNS records
quien dns github.com
# Output:
# {
# "domain": "github.com",
# "a": ["140.82.121.4"],
# "aaaa": [],
# "mx": [{"host": "aspmx.l.google.com", "priority": 1}],
# "ns": ["dns1.p08.nsone.net", "dns2.p08.nsone.net"],
# "txt": ["v=spf1 ip4:... ~all"],
# ...
# }
Mail configuration audit
quien mail example.com
# Returns SPF record, DMARC policy, DKIM selectors found,
# BIMI record, VMC certificate chain validation
TLS/SSL inspection
quien tls example.com
# Returns certificate subject, issuer, validity dates,
# SANs, chain depth, protocol version, cipher suite
HTTP headers
quien http example.com
# Returns status code, redirect chain, all response headers
# (Content-Security-Policy, HSTS, X-Frame-Options, etc.)
Tech stack detection
quien stack example.com
# Parses HTML for:
# - WordPress version + active plugins
# - JavaScript frameworks (React, Vue, Angular, Next.js, etc.)
# - CSS frameworks (Tailwind, Bootstrap, etc.)
# - External services (analytics, CDNs, payment providers)
IP address lookup
quien 8.8.8.8
quien --json 8.8.8.8
# Returns:
# - Reverse DNS (PTR record)
# - RDAP network block info
# - Abuse contact
# - Origin ASN + prefix (RDAP with BGP fallback)
# - PeeringDB enrichment: org, peering policy, IX/facility counts,
# traffic profile, peering locations
All data at once
quien all example.com | jq '.dns.a'
quien all example.com | jq '.mail.spf'
quien all example.com | jq '.tls.valid_until'
Scripting Patterns
Pipe JSON into jq
# Get all A records
quien dns example.com | jq '.a[]'
# Check if DMARC policy is reject
quien mail example.com | jq '.dmarc.policy == "reject"'
# Get certificate expiry
quien tls example.com | jq '.certificates[0].not_after'
# List detected JS frameworks
quien stack example.com | jq '.javascript_frameworks[]'
# Get ASN number for an IP
quien --json 1.1.1.1 | jq '.asn.number'
Shell script: bulk domain audit
#!/bin/bash
domains=("example.com" "github.com" "golang.org")
for domain in "${domains[@]}"; do
echo "=== $domain ==="
spf=$(quien mail "$domain" | jq -r '.spf.record // "MISSING"')
dmarc=$(quien mail "$domain" | jq -r '.dmarc.policy // "MISSING"')
tls_expiry=$(quien tls "$domain" | jq -r '.certificates[0].not_after // "N/A"')
echo "SPF: $spf"
echo "DMARC: $dmarc"
echo "TLS expires: $tls_expiry"
echo
done
Shell script: check domain expiry
#!/bin/bash
DOMAIN="${1:?Usage: $0 <domain>}"
expiry=$(quien --json "$DOMAIN" | jq -r '.whois.expires // .rdap.expires // empty')
if [ -z "$expiry" ]; then
echo "Could not determine expiry for $DOMAIN"
exit 1
fi
echo "$DOMAIN expires: $expiry"
Go integration — run quien as subprocess
package main
import (
"encoding/json"
"fmt"
"os/exec"
)
type DNSResult struct {
Domain string `json:"domain"`
A []string `json:"a"`
MX []struct {
Host string `json:"host"`
Priority int `json:"priority"`
} `json:"mx"`
TXT []string `json:"txt"`
}
func lookupDNS(domain string) (*DNSResult, error) {
out, err := exec.Command("quien", "dns", domain).Output()
if err != nil {
return nil, fmt.Errorf("quien dns failed: %w", err)
}
var result DNSResult
if err := json.Unmarshal(out, &result); err != nil {
return nil, fmt.Errorf("parse error: %w", err)
}
return &result, nil
}
func main() {
dns, err := lookupDNS("example.com")
if err != nil {
panic(err)
}
fmt.Printf("A records for %s: %v\n", dns.Domain, dns.A)
for _, mx := range dns.MX {
fmt.Printf("MX %d: %s\n", mx.Priority, mx.Host)
}
}
Go integration — full audit struct
package main
import (
"encoding/json"
"os/exec"
)
type FullAudit struct {
WHOIS struct {
Registrar string `json:"registrar"`
Created string `json:"created"`
Expires string `json:"expires"`
Updated string `json:"updated"`
} `json:"whois"`
DNS struct {
A []string `json:"a"`
NS []string `json:"ns"`
TXT []string `json:"txt"`
} `json:"dns"`
Mail struct {
SPF struct {
Record string `json:"record"`
Valid bool `json:"valid"`
} `json:"spf"`
DMARC struct {
Record string `json:"record"`
Policy string `json:"policy"`
} `json:"dmarc"`
} `json:"mail"`
TLS struct {
Certificates []struct {
Subject string `json:"subject"`
NotAfter string `json:"not_after"`
} `json:"certificates"`
} `json:"tls"`
Stack struct {
JavascriptFrameworks []string `json:"javascript_frameworks"`
CSSFrameworks []string `json:"css_frameworks"`
ExternalServices []string `json:"external_services"`
} `json:"stack"`
}
func auditDomain(domain string) (*FullAudit, error) {
out, err := exec.Command("quien", "all", domain).Output()
if err != nil {
return nil, err
}
var audit FullAudit
return &audit, json.Unmarshal(out, &audit)
}
Agent Skill Integration
Install quien as an agent skill so AI coding agents automatically use it for domain/IP lookups:
npx skills add retlehs/quien
Key Behaviors to Know
- RDAP-first: Uses RDAP protocol before falling back to raw WHOIS. Broader TLD coverage via IANA referral.
- Automatic retry: All network lookups use exponential backoff — transient failures are retried automatically.
- BGP fallback: If RDAP does not return ASN data for an IP, quien queries BGP routing tables for origin ASN/prefix.
- PeeringDB enrichment: ASN lookups are enriched with PeeringDB data (peering policy, IX presence, traffic profile).
- VMC validation: BIMI lookups validate the full VMC certificate chain, not just the record presence.
- Tech stack from HTML: Stack detection fetches and parses the actual HTML of the target, not just HTTP headers.
Troubleshooting
quien: command not found
# If installed via go install, ensure GOPATH/bin is in PATH
export PATH="$PATH:$(go env GOPATH)/bin"
TUI doesn't render correctly
# Ensure your terminal supports true color and UTF-8
echo $TERM # should be xterm-256color or similar
echo $LANG # should include UTF-8
Rate limiting / lookup failures
- RDAP and WHOIS servers may rate-limit. quien retries with backoff automatically.
- For bulk scripting, add
sleep 1between calls to avoid hitting rate limits.
No DKIM results
- DKIM requires knowing the selector. quien probes common selectors (google, default, mail, etc.) but custom selectors won't be discovered automatically.
IP lookup shows no ASN
- Some IP blocks are not in RDAP. quien falls back to BGP; if both fail, the block may be unrouted or private.
JSON output is empty / malformed
# Confirm the subcommand syntax — subcommands always output JSON
quien dns example.com # correct
quien --json dns example.com # incorrect - --json is for top-level only