README
¶
sondra
███████╗ ██████╗ ███╗ ██╗██████╗ ██████╗ █████╗ ██╔════╝██╔═══██╗████╗ ██║██╔══██╗██╔══██╗██╔══██╗ ███████╗██║ ██║██╔██╗ ██║██║ ██║██████╔╝███████║ ╚════██║██║ ██║██║╚██╗██║██║ ██║██╔══██╗██╔══██║ ███████║╚██████╔╝██║ ╚████║██████╔╝██║ ██║██║ ██║ ╚══════╝ ╚═════╝ ╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝
Automated bug-bounty recon pipeline. Named after sonder — the realization that every target has hidden depth.
sondra orchestrates ~a dozen recon tools into one fast, single-binary Go pipeline: passive/active subdomain enumeration → DNS resolution → HTTP probing → port scan → URL collection → JS endpoint/secret extraction → screenshots → nuclei vuln scan — then a filterable HTML report on every run. It runs as an interactive TUI, a headless daemon for a VPS/cron/CI, a continuous monitor that only alerts on change, or a Telegram/Discord control bot you drive from your phone.
Features
- 🧩 One pipeline, ~a dozen tools — subfinder, assetfinder, crt.sh, alterx, massdns, dnsx, httpx, naabu, gau, gowaybackgo, katana, gowitness, nuclei — with per-module control.
- 🖥️ Fixed TUI header that never scrolls, goroutine-parallel enumeration, live per-step progress.
- 🤖 Headless & JSON modes for VPS / cron / CI (auto-enabled when stdout isn't a terminal).
- ⏱️ Two-phase delivery — recon results (and the report) land in ~minutes; the slow nuclei phase runs after and updates the report.
- 🔁 Continuous monitoring — rescan on a loop and alert only when something changes.
- 🔎 JS analysis — fetch collected
.jsand extract endpoints + secrets (API keys, tokens, JWTs). - 🔔 Notifications — Discord embeds, Telegram, or a generic JSON webhook; real-time per-finding alerts + a structured payload for automation.
- 📱 Control bot — Telegram text commands and Discord slash commands, allow-listed, with monitors that survive a restart.
- 📄 Filterable HTML report on every run, plus a subdomain diff vs the previous run.
Table of Contents
- Install
- Quick start
- Commands
- Usage
- Presets
- Modules
- Interactive selector
- Config file
- Notifications
- Control bot (Telegram + Discord)
- Dependencies
- Output structure
- Report
- Releasing a new version
- License
Install
go install github.com/AliMousaviSoft/sondra/cmd/sondra@latest
Or grab a prebuilt binary from Releases:
# Linux x86_64
curl -Lo sondra.tar.gz https://github.com/AliMousaviSoft/sondra/releases/latest/download/sondra_linux_amd64.tar.gz
tar xzf sondra.tar.gz
sudo mv sondra /usr/local/bin/
Prebuilt binaries are published for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, and windows/amd64. Then install the external tools for the modules you plan to use.
Quick start
sondra scan -d target.com # interactive module picker, then scan
sondra scan -d target.com -p quick # quick preset, no prompt
sondra monitor -d target.com --interval 6h # watch the attack surface, alert on change
sondra bot # run the Telegram/Discord control bot
Commands
| Command | Purpose | Key flags |
|---|---|---|
scan |
Run the recon pipeline once | -d, -p, -e, -y, --modules, --resume, --headless, --json |
monitor |
Loop scans, alert only on deltas | -d, -p, -e, --interval, --once, --on, --modules, --json |
bot |
Telegram/Discord control daemon | --config |
report |
Regenerate the HTML report | -d, --dir |
diff |
Show new subdomains vs the last run | -d |
doctor |
Preflight-check tools, templates, and config | — |
version |
Print version + update status | — |
update |
Check for / apply a self-update | — |
Common flags:
| Flag | Applies to | Meaning |
|---|---|---|
-d, --domain |
scan/monitor/report/diff | Target domain (required) |
-p, --preset |
scan/monitor | full | quick | passive | enum | vuln |
-e, --exclude |
scan/monitor | Subdomain(s) to drop from results (repeatable) |
--modules |
scan/monitor | Exact module list, overrides the preset (skips the selector) |
-y, --yes |
scan | Skip the interactive selector (use the preset as-is) |
--resume |
scan | Reuse the latest run dir for this domain — skip already-completed steps |
--headless |
scan | No TUI — structured stdout for VPS/cron/CI |
--json |
scan/monitor | Newline-delimited JSON logs (implies headless) |
--once |
monitor | Single pass then exit (for cron/systemd) |
--on |
monitor | Alert trigger: all | assets | findings |
--interval |
monitor | Time between passes, e.g. 30m, 6h |
--config |
all | Config file path (default ~/.sondra.yaml) |
Usage
sondra scan -d target.com # interactive module selector
sondra scan -d target.com -p quick # quick preset, skip selector
sondra scan -d target.com -y # full preset, no prompt
sondra scan -d target.com -p enum -y # enum-only preset
sondra scan -d target.com -e dev.target.com # exclude a noisy subdomain
sondra scan -d target.com --modules gowayback,jsanalysis # passive URL + JS only
sondra scan -d target.com -p full -y --resume # continue the last run — skip work already done
sondra scan -d target.com -p quick --headless # no TUI — for VPS / cron / CI
sondra scan -d target.com -p quick --json # newline-delimited JSON logs
sondra monitor -d target.com --interval 6h # rescan on a loop, alert on changes
sondra monitor -d target.com --once # single monitor pass (for cron)
sondra bot # Telegram/Discord control bot
sondra report -d target.com # regenerate HTML report
sondra diff -d target.com # show new subdomains vs last run
sondra version # show version + update status
sondra update # check for updates
Continuous monitoring
sondra monitor rescans a domain on a loop and only alerts when something changes vs the previous run — new subdomains, live hosts, open ports, takeovers, or nuclei findings. It's the way to watch a program's attack surface hands-free.
sondra monitor -d target.com --interval 6h # loop every 6h (default)
sondra monitor -d target.com --once # one pass, then exit (cron/systemd)
sondra monitor -d target.com --on findings # alert only on new vulns
sondra monitor -d target.com --on assets # alert only on new subdomains/hosts/ports
sondra monitor -d target.com --modules subfinder,crtsh,httpx # custom module set
--on picks what counts as notable: all (default), assets, or findings. Each pass runs a full headless scan, diffs it against the last run, and sends a single delta notification (nothing is sent when nothing changed). Pair --once with cron/systemd if you'd rather not keep a process running. The first run just establishes the baseline (no alert).
Headless mode
--headless runs the full pipeline without the interactive TUI, streaming structured log/progress lines to stdout — ideal for a VPS, cron job, or CI. It is selected automatically when stdout is not a terminal (piped or redirected), and --json (which implies headless) emits newline-delimited JSON for machine consumption. SIGINT/SIGTERM cancels a run cleanly. Modules are resolved from -p/--preset or --modules (no selector prompt).
Reusing a run (--resume)
By default every scan starts a fresh recon-<timestamp> directory and re-runs everything. Pass --resume to instead continue the most recent run dir for the domain: steps whose output is still fresh (within cache_age, default 24h) are skipped, so only new or stale modules run.
sondra scan -d target.com -p quick -y # → recon-A
sondra scan -d target.com -p full -y --resume # reuses recon-A: subfinder/httpx cached, only the extra full modules run
Notes: --resume reuses the merged passive-enum / URL-collection outputs, so resuming a smaller preset into a bigger one skips the extra passive sources — run without --resume for a full re-enumeration. monitor and diff always use fresh dirs (they compare consecutive runs), so they're unaffected.
Presets
| Preset | Modules |
|---|---|
full |
everything (enum + resolve + httpx + naabu + URL collection + jsanalysis + gowitness + nuclei) |
quick |
subfinder + crt.sh + httpx + nuclei crit/high |
passive |
subfinder + assetfinder + crt.sh |
enum |
passive + alterx + massdns + httpx + naabu |
vuln |
nuclei crit/high + medium (background) |
Override any preset with --modules a,b,c.
Modules
Select any subset with --modules (comma-separated) or interactively. Valid names (and aliases):
| Module | Type | What it does | Needs |
|---|---|---|---|
subfinder |
Go lib | passive subdomain enum via APIs | — |
assetfinder |
binary | subdomains from cert transparency + web archives | — |
crtsh |
HTTP | certificate-transparency subdomains | — |
alterx |
binary | permutation-based subdomain generation | enum output |
massdns |
binary | high-speed DNS brute force | wordlist |
httpx |
binary | HTTP probe: alive check + title/tech/status/server | resolved hosts |
naabu (ports) |
binary | port scan for non-standard ports | resolved hosts |
takeover (takeovers) |
binary (nuclei) | subdomain-takeover fingerprinting | live hosts |
gau |
binary | URLs from Wayback / CommonCrawl | domain |
gowayback (wayback, gowaybackgo) |
binary | Wayback CDX URLs (better coverage than gau) | domain |
katana |
binary | active crawl + JS endpoint extraction | live hosts |
jsanalysis (js, secrets) |
HTTP + regex | extract endpoints + secrets from collected JS | js_urls.txt |
gowitness (screenshots) |
binary | headless screenshots of live hosts | live hosts |
nuclei |
binary | vuln scan (crit/high + medium) — runs after recon | live hosts |
nuclei-high |
binary | nuclei crit/high only | live hosts |
nuclei-medium |
binary | nuclei medium (async background) | live hosts |
Pipeline order matters. A resolve step (dnsx) runs automatically whenever an enum source is selected, feeding httpx. Modules that need live/resolved hosts (httpx, naabu, nuclei, takeover, gowitness, katana) therefore need an enum source (subfinder/crtsh/assetfinder/…) upstream. The passive URL/JS modules are the exception: gau and gowayback query the domain directly, and jsanalysis reads the collected .js list — so --modules gowayback,jsanalysis is a valid standalone run with no enumeration.
JS endpoint & secret analysis
The jsanalysis module fetches the .js files that URL collection classifies into wayback-data/js_urls.txt, then runs two extractors over each body (bounded: up to 500 files, 5 MB each):
- Endpoints →
js-analysis/endpoints.txt— absolute URLs and rooted API paths (/api/v2/users?…), with static assets (css/img/fonts/maps) filtered out. - Secrets →
js-analysis/secrets.txt— AWS access keys, Google API/OAuth keys, Slack tokens & webhooks, GitHub tokens, Stripe keys, JWTs, private-key blocks, and genericapi_key/tokenassignments. Secrets are logged as a warning because they're worth a look.
It needs a URL source to have JS to analyze, so pair it with a collector:
sondra scan -d target.com --modules gowayback,jsanalysis # passive
sondra scan -d target.com -p full # jsanalysis included
Interactive selector
Run scan without -y/--modules to get the full module picker:
sondra scan -d target.com
| Key | Action |
|---|---|
↑↓ / jk |
navigate |
space |
toggle module |
a |
select all available |
n |
deselect all |
1 |
full preset |
2 |
quick preset |
3 |
passive preset |
4 |
enum preset |
5 |
vuln preset |
enter |
start scan |
q |
quit |
Modules whose external binary isn't installed are shown struck-through and can't be selected.
Config file
~/.sondra.yaml or .sondra.yaml in the current directory. Settings merge in ascending priority: defaults → ~/.sondra.yaml → ./.sondra.yaml → environment (SONDRA_ prefix) → CLI flags.
concurrency: 50
rate_limit: 150
wayback_rate: 2 # gowaybackgo CDX requests/sec — raise for speed, lower (1) if you hit 429s
timeout: 10s
cache_age: 24h
resolvers_file: /opt/resolvers.txt
nuclei_templates: ~/nuclei-templates
# Notifications (optional) — any channel with credentials is used.
notify:
discord: https://discord.com/api/webhooks/xxx/yyy # Discord webhook URL
telegram:
token: "123456:ABC-DEF..." # bot token from @BotFather
chat_id: "987654321" # your chat/user id
webhook: https://example.com/hook # generic JSON POST (Slack/ntfy/bot)
min_severity: high # lowest nuclei severity listed in summaries
on_start: false # also notify when a scan starts (default off)
only_notable: false # only send finish msg on signal (findings/new subs/takeovers/error)
# Control bot (optional) — drive scans/monitors from Telegram and/or Discord
bot:
token: "123456:ABC..." # Telegram (falls back to notify.telegram.token)
allowed_users: "111111111,222222222" # Telegram user ids allowed
discord_token: "..." # Discord bot token (slash commands)
discord_users: "333333333,444444444" # Discord user ids allowed
discord_guild: "555555555" # optional: instant per-guild command registration
state_db: "sondra-bot.db" # SQLite path for resuming monitors after a restart ("off" disables)
Environment variables use the SONDRA_ prefix (nested keys join with _):
export SONDRA_CONCURRENCY=100
export SONDRA_RATE_LIMIT=200
export SONDRA_NOTIFY_DISCORD="https://discord.com/api/webhooks/xxx/yyy"
export SONDRA_NOTIFY_TELEGRAM_TOKEN="123456:ABC-DEF..."
export SONDRA_NOTIFY_TELEGRAM_CHAT_ID="987654321"
export SONDRA_NOTIFY_WEBHOOK="https://example.com/hook"
export SONDRA_BOT_TOKEN="123456:ABC..."
export SONDRA_BOT_ALLOWED_USERS="111111111"
export SONDRA_BOT_DISCORD_TOKEN="..."
export SONDRA_BOT_DISCORD_USERS="333333333"
Notifications
Nuclei runs after the recon phase, so sondra delivers results in minutes instead of blocking ~15–20 m on the vuln scan:
- Recon done — once subdomains/live-hosts/URLs are collected, the report is generated and an interim alert fires (
✅ Recon done — 🔍 scanning vulns…) with subdomain/live/URL counts, new subdomains, and high-value hosts. - Scan complete — nuclei then runs in the background; when it finishes the report is regenerated with findings and the final alert fires with nuclei findings by severity (🔴🟠🟡⚪), takeovers, a list of findings at or above
min_severity, and a link to the report. Its colour and title track the worst thing found (🚨 critical / ⚠️ high / ✅ clean).
(A recon-only run with no nuclei modules sends just the one finish message. If notify.on_start: true, a start message fires too.)
To keep a busy channel quiet, set notify.only_notable: true — the finish message is then sent only when there's signal (a finding ≥ min_severity, a new subdomain, a takeover, or an error); clean runs stay silent.
| Channel | Config keys | Format |
|---|---|---|
| Discord | notify.discord |
coloured embed |
| Telegram | notify.telegram.token + .chat_id |
plain-text message |
| Webhook | notify.webhook |
raw JSON POST (catch-all) |
The finish embed also includes a status-code breakdown and the top technologies seen across live hosts. Delivery is best-effort: a failing channel logs a warning and never aborts the scan. Works in both TUI and headless modes.
Real-time per-finding alerts
Each nuclei finding at or above min_severity is pushed the moment nuclei discovers it (not just in the end-of-scan summary), so a live critical reaches you immediately.
Structured webhook payload (for bots / automation)
The generic notify.webhook receives, in addition to the display fields, a result object with the full structured scan — ready for a bot or automation backend to consume:
{
"title": "✅ Scan complete: target.com",
"result": {
"domain": "target.com",
"event": "scan_complete",
"stats": { "subdomains": 31, "live_hosts": 10, "findings": 0 },
"findings": [ { "template_id": "...", "severity": "high", "host": "..." } ],
"new_subdomains": [ "..." ],
"high_value": [ "..." ],
"report_path": "target.com/recon-.../master_report.html"
}
}
monitor sends the same payload with "event": "monitor_delta" and new_* arrays describing only what changed. Discord/Telegram ignore result and render the human-readable message.
Control bot (Telegram + Discord)
sondra bot runs a daemon so you can drive scans and monitors from chat — Telegram (text commands) and/or Discord (native slash commands). Both are allow-listed (only the configured user ids can command it) and validate every target against a strict domain regex — input flows into the recon tools, so this is the injection gate.
# Telegram
export SONDRA_BOT_TOKEN="123456:ABC..." # or notify.telegram.token
export SONDRA_BOT_ALLOWED_USERS="111111111" # your Telegram user id
# Discord (optional)
export SONDRA_BOT_DISCORD_TOKEN="..." # Discord bot token
export SONDRA_BOT_DISCORD_USERS="333333333" # your Discord user id
sondra bot # starts whichever is configured
Commands (Telegram text · Discord slash):
/scan <domain> [preset] [--modules …] [--exclude …]
/monitor <domain> [--interval 6h] [--on all|assets|findings] [--modules …]
/diff [domain] /status /stop <id> /stopall
/report [domain] /presets /modules /version /help
- Results (recon-done, per-finding, finish alerts) stream back to the chat/channel that issued the command and to your configured Discord/webhook.
- Jobs run in the background — start several, watch with
/status(aliases/list,/jobs), cancel with/stop <id>or/stopall. /reportand/diffdefault to the most recently scanned domain when you omit it./reportuploads the actualmaster_report.htmlas a file attachment (not a host-side path), so you can open it straight from your phone. It picks the latest finished run, skipping any scan still in progress.
Monitors survive restarts. Running monitors are persisted to a SQLite file (bot.state_db, default sondra-bot.db; set off to disable). When the daemon restarts it resumes each one — replying in the original chat/channel with a ♻️ Resumed monitor #N note — so a redeploy or crash won't silently drop a long-running watch. A user /stop (or /stopall) forgets the job; only a shutdown keeps it for resume. One-shot /scan jobs are not persisted. The store uses a pure-Go SQLite driver, so release binaries stay cgo-free.
Telegram setup: message @BotFather → /newbot (or reuse your notify bot) for the token; message @userinfobot for your user id.
Discord setup: Developer Portal → New Application → Bot → copy the token → invite it to your server. For slash commands the applications.commands scope is enough; add the bot scope with Send Messages if you want streamed scan results and resumed monitors to post beyond the first ~15 minutes. Slash commands register on startup — set discord_guild for instant registration (global takes up to ~1 h to appear).
Dependencies
Go dependency (compiled in — no install needed)
subfinder/v2— passive subdomain enumeration.
crt.sh and jsanalysis are plain HTTP + regex, also always available.
External tools (exec — install separately)
Run sondra doctor to see what's installed, tool versions, which presets are runnable, and whether templates/notify/bot config are set up — it exits non-zero if anything's missing (handy for CI/cron):
$ sondra doctor
Recon tools
✓ httpx 1.9.0
✗ naabu NOT FOUND → go install github.com/projectdiscovery/naabu/v2/cmd/naabu@latest
Presets
✗ enum needs naabu
sondra also checks for a selected module's binary at runtime and warns if it's missing:
# DNS + HTTP probing
go install github.com/projectdiscovery/dnsx/cmd/dnsx@latest
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
go install github.com/projectdiscovery/naabu/v2/cmd/naabu@latest
# Subdomain enum
go install github.com/tomnomnom/assetfinder@latest
go install github.com/projectdiscovery/alterx/cmd/alterx@latest
# Crawling + URL collection
go install github.com/projectdiscovery/katana/cmd/katana@latest
go install github.com/lc/gau/v2/cmd/gau@latest
go install -v github.com/OoS-MaMaD/gowaybackgo@latest # Wayback CDX (better coverage than gau/waybackurls)
# Screenshots
go install github.com/sensepost/gowitness@latest
# Vulnerability scanning
go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
nuclei -update-templates
# DNS brute force (C binary) — https://github.com/blechschmidt/massdns
Output structure
target.com/
└── recon-2026-05-24_13-50/
├── passive_subs.txt ← raw enum output
├── alldomains.txt ← merged + deduped
├── resolved.txt ← dnsx confirmed live
├── live.txt ← httpx alive hosts
├── httpx_full.json ← enriched: title, tech, status
├── high_value.txt ← admin/api/dev targets
├── new_subdomains.txt ← diff from previous run
├── subfinder.txt
├── master_report.html ← filterable HTML report
├── naabu-output/
│ └── open_ports.txt
├── wayback-data/
│ ├── all_urls.txt
│ ├── js_urls.txt
│ ├── php_urls.txt
│ ├── aspx_urls.txt
│ ├── param_wordlist.txt
│ └── endpoints.txt
├── js-analysis/
│ ├── endpoints.txt ← paths/URLs extracted from JS
│ └── secrets.txt ← API keys/tokens found in JS
├── gowitness-output/
│ └── screenshots/
└── nuclei-results/
├── crit_high.jsonl
├── medium.jsonl
└── takeovers.txt
Report
The HTML report at master_report.html includes:
- Stats grid: subdomains, live hosts, nuclei findings, takeovers, URLs, ports, high-value targets
- Nuclei findings table sorted by severity (critical → high → medium)
- High-value targets section
- New subdomains diff from the previous run
- Live hosts table with status-code filters, tech detection, web server
- Open non-standard ports
- Subdomain takeover candidates
Regenerate it any time with sondra report -d target.com (add --dir to target a specific run).
Releasing a new version
git tag v1.x.x
git push origin v1.x.x
# goreleaser builds and publishes binaries automatically via GitHub Actions
Builds binaries for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, and windows/amd64.
Tip: when publishing multiple tags, push the highest version last — GitHub marks whichever release publishes last as Latest.
License
MIT
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
sondra
command
|
|
|
internal
|
|
|
bot
Package bot implements the sondra control bot: it lets allow-listed users drive scans, monitors, and diffs from Telegram (text commands) and Discord (slash commands), streaming results back through the notification system.
|
Package bot implements the sondra control bot: it lets allow-listed users drive scans, monitors, and diffs from Telegram (text commands) and Discord (slash commands), streaming results back through the notification system. |
|
doctor
Package doctor implements `sondra doctor`: a preflight check of the external tools, nuclei templates, and config that sondra needs before a scan, so a missing dependency is reported up front instead of failing mid-run.
|
Package doctor implements `sondra doctor`: a preflight check of the external tools, nuclei templates, and config that sondra needs before a scan, so a missing dependency is reported up front instead of failing mid-run. |
|
notify
Package notify delivers scan lifecycle events to external channels (Discord, Telegram, generic webhooks).
|
Package notify delivers scan lifecycle events to external channels (Discord, Telegram, generic webhooks). |