AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Domain Expired Opportunity Finder

skill-varnan-tech-opendirectory-domain-expired-opportunity-finder · by Varnan-Tech

Evaluates expired domain candidates against a target niche, scores them by topical relevance, historical activity level, and history cleanliness, then outputs a ranked shortlist with explainable reasoning and risk flags.

No reviews yet
0 installs
6 views
0.0% view→install

Install

$ agentstack add skill-varnan-tech-opendirectory-domain-expired-opportunity-finder

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-varnan-tech-opendirectory-domain-expired-opportunity-finder)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Domain Expired Opportunity Finder? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Expired Domain Opportunity Finder

Evaluate expired domain candidates for a specific niche. Score them on topical fit, historical activity level, history cleanliness, and redirect suitability. Output a conservative, explainable shortlist for human review.


Critical rule: Every recommendation must include BOTH a positive rationale (why_selected) AND a caution rationale (why_risky). Never output a bare score without explanation.

Conservative-by-default rule: When signals are incomplete or contradictory, lower the confidence level. Do not surface ambiguous candidates as strong opportunities. Missing data reduces confidence, never inflates it.

Anti-abuse rule: Never encourage unrelated redirects, PBN construction, or domain repurposing where the historical topic does not match the target niche. Read references/guardrails.md for the full anti-abuse policy.


Step 1: Setup Check

Check the environment before doing anything else.

Verify that curl and python3 (or python) are available:

curl --version > /dev/null 2>&1 && echo "curl: available" || echo "curl: MISSING"
python3 --version 2>/dev/null || python --version 2>/dev/null || echo "python: MISSING"

Check for an optional LLM API key for enhanced niche-relevance scoring:

echo "LLM_API_KEY: ${LLM_API_KEY:+set}"

If curl or python is missing: Stop. Tell the user: "This skill requires curl and Python 3.10+. Please install them and try again."

If LLM_API_KEY is not set: Continue. The skill will use rule-based scoring only (domain string matching, Wayback title analysis, keyword overlap). Note to the user: "Running in rule-based-only mode. Set LLMAPIKEY for enhanced niche-relevance scoring."

If LLM_API_KEY is set: The skill will use LLM-enhanced scoring for topical relevance analysis. This provides deeper contextual assessment of niche fit.

QA: State the scoring mode (llm-enhanced or rule-based-only) and confirm tools are available.


Step 2: Input Collection

Collect the required and optional inputs from the user.

Required:

  • target_niche (string): The core niche to evaluate against. Examples: "developer tools", "AI SaaS", "cybersecurity", "fintech".

Optional (ask only if not provided):

  • seed_keywords (array): Keywords to refine topical matching. If not provided, extract 3–5 keywords from the niche name automatically.
  • candidate_domains (array): Specific domains to evaluate. If not provided, prompt the user.
  • discovery_source (string): Where candidates came from — manual, expireddomains-net, external-feed.
  • min_snapshots (integer): Minimum historical snapshot threshold. Default: 10.
  • max_risk_level (string): low, medium, or high. Controls how aggressively risky candidates are filtered. Default: medium.
  • intended_use (string): rebuild, redirect, or either. Default: either.

If no candidate_domains are provided: Ask: "Please provide a list of expired domain candidates to evaluate. You can:

  1. Paste domain names (one per line or comma-separated)
  2. Provide a file path to a text file with one domain per line
  3. Say 'example' to run with a built-in demo set for the 'developer tools' niche"

If the user says 'example': Use this demo set:

devtoolsweekly.com
codeshipnews.io
stackforgeapp.com
quickseorank.net
bestcheaphosting247.com
cloudbuildpro.dev
reactwidgetlib.com
megadealsshop.xyz

After collecting all inputs, confirm: "Target niche: [niche]. Evaluating [N] candidate domains. Scoring mode: [mode]. Intended use: [use]."


Step 3: Candidate Normalization

Clean and validate the candidate list before scoring.

python3 -c "
import sys, re

domains = '''CANDIDATE_LIST_HERE'''.strip().split('\n')
seen = set()
valid = []
invalid = []

for d in domains:
    d = d.strip().lower()
    # Strip protocols and paths
    d = re.sub(r'^https?://', '', d)
    d = d.split('/')[0]
    d = d.strip('.')

    if not d:
        continue

    # Basic TLD validation
    if '.' not in d or len(d)  0 snapshots, fetch the most recent snapshot to extract
the page title (used for topical relevance scoring):

```bash
curl -s -L "https://web.archive.org/web/LATEST_TIMESTAMP/http://DOMAIN_HERE" \
  | python3 -c "
import sys, re
html = sys.stdin.read()[:50000]
title_match = re.search(r']*>(.*?)', html, re.IGNORECASE | re.DOTALL)
title = title_match.group(1).strip() if title_match else 'no title found'
# Extract meta description too
meta_match = re.search(r']*name=[\"']description[\"'][^>]*content=[\"'](.*?)[\"']', html, re.IGNORECASE)
desc = meta_match.group(1).strip() if meta_match else 'no description found'
print(f'Title: {title}')
print(f'Description: {desc}')
"

Replace LATEST_TIMESTAMP with the most recent timestamp from Step 4a.

4c: RDAP Lookup — Registration Status

Use the cross-platform HTTP-based RDAP standard (replaces OS-dependent WHOIS). An HTTP 404 from RDAP means the domain is not registered (i.e. it is genuinely available or untracked) — that is distinct from a network failure. Handle both cases explicitly:

python3 -c "
import urllib.request, urllib.error, json

domain = 'DOMAIN_HERE'
try:
    req = urllib.request.Request(
        f'https://rdap.org/domain/{domain}',
        headers={'User-Agent': 'Mozilla/5.0'}
    )
    with urllib.request.urlopen(req, timeout=10) as response:
        data = json.loads(response.read().decode())

    registrar = 'unknown'
    created = 'unknown'

    for entity in data.get('entities', []):
        if 'registrar' in entity.get('roles', []):
            try:
                registrar = entity.get('vcardArray', [[]])[1][0][3]
            except Exception:
                pass

    for event in data.get('events', []):
        if event.get('eventAction') == 'registration':
            created = event.get('eventDate', 'unknown')

    print(json.dumps({
        'domain': domain,
        'status': 'registered',
        'registrar': registrar,
        'created': created
    }))
except urllib.error.HTTPError as e:
    if e.code == 404:
        # Domain has no RDAP object — likely unregistered or not in RDAP coverage
        print(json.dumps({'domain': domain, 'status': 'unregistered_or_no_rdap_object'}))
    else:
        print(json.dumps({'domain': domain, 'error': f'rdap_http_error_{e.code}'}))
except Exception:
    print(json.dumps({'domain': domain, 'error': 'rdap_lookup_failed'}))
"

4d: Domain String Analysis — Keyword Matching

Score keyword overlap between the domain name and the target niche / seed keywords:

python3 -c "
import re, json

domain = 'DOMAIN_HERE'
niche = 'NICHE_HERE'
seeds = SEEDS_JSON_HERE  # e.g., ['devops', 'ci/cd', 'code editor']

# Extract words from domain
domain_base = domain.rsplit('.', 1)[0]  # remove TLD
domain_words = re.split(r'[-_.]', domain_base.lower())

# Check niche words
niche_words = niche.lower().split()
all_keywords = set(niche_words + [s.lower() for s in seeds])

matches = [w for w in domain_words if any(kw in w or w in kw for kw in all_keywords)]
match_ratio = len(matches) / max(len(domain_words), 1)

print(json.dumps({
    'domain': domain,
    'domain_words': domain_words,
    'keyword_matches': matches,
    'match_ratio': round(match_ratio, 2)
}))
"

4e: Gemini LLM Niche-Relevance Assessment (if LLMAPIKEY is set)

If the LLM API key is configured, batch all candidates with their collected signals and ask for a contextual niche-relevance assessment.

Note: The request/response format below uses the Gemini API (generateContent format). It is not compatible with OpenAI-style endpoints without modification. If you use a different provider, you must adapt the JSON body and response parsing.

cat > /tmp/domain-relevance-request.json  "$OUTFILE" << 'EOF'
JSON_OUTPUT_HERE
EOF
echo "Saved to $OUTFILE"

If 0 candidates pass the shortlist: "No candidates met the shortlist criteria for the '[niche]' niche with the current risk tolerance. This is a normal outcome — it means the evaluated domains were not strong enough matches. Try:

  1. Providing different candidate domains
  2. Widening seed keywords
  3. Setting maxrisklevel to 'high' to see borderline candidates
  4. Running in audit mode to see why candidates were rejected"

Self-QA Checklist

Run every check before presenting output:

  • [ ] Every shortlisted domain has both why_selected AND why_risky
  • [ ] No shortlisted domain has a High-severity risk flag AND high-priority-review action
  • [ ] Domains with redirect_mismatch are labeled rebuild-only-review (not review)
  • [ ] The guardrails disclaimer is present at the end of output
  • [ ] No hype language: no "guaranteed", "easy win", "safe to redirect", "SEO hack"
  • [ ] scoring_mode correctly reflects whether LLM was used
  • [ ] Candidates are ranked by opportunity_score descending
  • [ ] JSON output saved to docs/expired-domain-intel/YYYY-MM-DD.json
  • [ ] All Wayback API calls were rate-limited (2s between calls)

Fix any violation before presenting.


What Good Output Looks Like

  • Every domain has a score, confidence, action, and risk assessment
  • Summaries are 1–2 sentences each, specific to the candidate (not generic)
  • Risk flags are present and explained in why_risky
  • The shortlist is small (quality over quantity) — typically 2–5 domains from a batch of 10–20
  • Conservative: when in doubt, reject or lower confidence
  • The user can understand exactly why each domain was selected or rejected

What Bad Output Looks Like

  • Bare scores without explanation
  • Generic summaries like "this domain has good metrics" (must be specific)
  • High-priority recommendations for domains with serious risk flags
  • Redirect recommendations for topic-mismatched domains
  • No disclaimer at the end
  • Hype language promising SEO outcomes
  • Too many shortlisted domains (the skill should be selective, not permissive)

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.