README
¶
Fast concurrent subdomain enumeration, written in Go. Point it at a domain and a wordlist; it brute-forces DNS across a worker pool and prints the subdomains that resolve. Built for pentesters, bug-bounty hunters, and operators doing authorized reconnaissance of their own infrastructure.
Quick Start | Configuration | Usage | Architecture | Changelog
[!IMPORTANT] Authorized use only. Only scan domains you own or have explicit written permission to test. Unauthorized scanning may violate applicable laws. Users are solely responsible for compliance with all applicable laws and regulations.
Quick Start
git clone https://github.com/TMHSDigital/subenum.git
cd subenum
go build -buildvcs=false -o subenum
./subenum -w examples/sample_wordlist.txt example.com
No network required to try it out. Simulation mode generates synthetic results with zero DNS queries:
./subenum -simulate -hit-rate 20 -w examples/sample_wordlist.txt example.com
Or launch the interactive terminal UI with no flags:
./subenum -tui
Feature Matrix
| Module | Description |
|---|---|
| Worker Pool | Spawn N goroutines for parallel DNS resolution with a configurable concurrency ceiling |
| DNS Engine | Resolve subdomains against any DNS server with per-query timeouts and retry backoff |
| Wildcard Detection | Double-probe check before scanning; aborts early unless -force is set |
| Graceful Shutdown | Trap SIGINT/SIGTERM, drain in-flight workers, flush partial results |
| Input Validation | RFC-compliant domain syntax and strict ip:port format enforcement |
| Wordlist Dedup | Deduplicate wordlist entries in a single pass before scanning begins |
| Simulation Mode | Generate synthetic DNS results at a configurable hit rate, with zero network I/O |
| Output Pipeline | Resolved domains to stdout (pipe-clean); progress and diagnostics to stderr |
| Output Formats | Emit results as text, json (array of subdomain plus typed records), or csv via -format |
| Rate Limiting | Cap total DNS queries per second across the worker pool with -rate (context-aware) |
| Record Types | Look up and filter by A, AAAA, or CNAME records with -type |
| Recursive Enumeration | Enumerate subdomains of discovered subdomains with -recursive and a -depth cap, with loop and duplicate protection |
| Interactive TUI | Form-based config and live-scrolling results via -tui; session values persisted |
System Architecture
flowchart LR
subgraph Input
A[Wordlist File] -->|"dedup + load"| B(Entry Slice)
C[CLI Flags / TUI Form] --> D(Argument Parser)
end
subgraph PreScan
D --> W{Wildcard\nDetection}
W -->|"no wildcard / -force"| E
end
subgraph Engine
B --> E{Worker Pool\nN Goroutines}
E -->|subdomain.domain| F[DNS Resolver]
F -->|retry + backoff| F
G[Context] -->|cancel| E
G -->|timeout| F
end
subgraph OutputLayer ["Output"]
F -->|resolved| H["stdout (results)"]
F -->|resolved| I[Output File]
E -->|atomic counters| J["stderr (progress)"]
end
K[SIGINT / SIGTERM] -->|cancel| G
Installation
Prerequisites: Go 1.24.2+ · Git · Make (optional) · Docker (optional)
Build from source
git clone https://github.com/TMHSDigital/subenum.git
cd subenum
go build -buildvcs=false -o subenum
Pre-built binaries
Download the appropriate binary for your platform from the Releases page.
Platforms: Linux (amd64, arm64) · macOS (amd64, arm64) · Windows (amd64)
SHA-256 checksums are provided alongside each binary.
Docker
docker build -t subenum .
docker run --rm -v $(pwd)/data:/data subenum -w /data/wordlist.txt example.com
Or with Compose:
docker compose run subenum
Make targets
make build # compile binary
make test # run test suite with race detector
make lint # run golangci-lint
make simulate # safe run - no DNS queries
make tui # launch interactive TUI
make docker-build # build Docker image
make help # list all targets
Configuration
CLI flags
| Flag | Default | Description |
|---|---|---|
-w <file> |
n/a | Wordlist file, one prefix per line (required) |
-t <n> |
100 |
Concurrent worker goroutines |
-timeout <ms> |
1000 |
Per-query DNS timeout in milliseconds |
-dns-server <ip:port> |
8.8.8.8:53 |
DNS server address (validated on startup) |
-attempts <n> |
1 |
DNS resolution attempts per subdomain (1 = no retry) |
-force |
false |
Continue scanning even if wildcard DNS is detected |
-o <file> |
n/a | Write results to file in addition to stdout |
-format <fmt> |
text |
Output format: text, json, or csv |
-rate <qps> |
0 |
Max DNS queries per second across all workers (0 = unlimited) |
-type <list> |
A,AAAA |
Comma-separated record types to look up: A, AAAA, CNAME |
-recursive |
false |
Recursively enumerate subdomains of discovered subdomains |
-depth <n> |
1 |
Max recursion depth when -recursive is set (1 = no recursion) |
-v |
false |
Verbose output: IPs, timings, per-query detail (stderr) |
-progress |
true |
Live progress line on stderr |
-simulate |
false |
Simulation mode: no real DNS queries |
-hit-rate <n> |
15 |
Simulated resolution rate, percent (1-100) |
-tui |
false |
Launch the interactive Terminal UI |
-version |
n/a | Print version and exit |
-retries <n> |
n/a | Deprecated - alias for -attempts, prints a warning |
[!NOTE] Wildcard DNS is detected automatically before scanning begins. If the target resolves wildcard records, the tool exits with a warning, since all subdomains would match, making results meaningless. Pass
-forceto override.
[!CAUTION] Simulation mode (
-simulate) generates synthetic results and performs zero network I/O. Do not confuse simulated output with real DNS data.
Usage
CLI
subenum -w <wordlist> [flags] <domain>
Examples
Basic scan
./subenum -w wordlist.txt example.com
High-throughput with Cloudflare DNS, saving results
./subenum -w wordlist.txt -t 300 -timeout 500 -dns-server 1.1.1.1:53 -o results.txt example.com
Resilient scan for flaky networks
./subenum -w wordlist.txt -attempts 3 -timeout 2000 example.com
Pipe-friendly - only resolved subdomains on stdout
./subenum -w wordlist.txt example.com | cut -d' ' -f2 | your-takeover-scanner
Force scan on a wildcard domain
./subenum -w wordlist.txt -force example.com
Simulation - zero network I/O
./subenum -simulate -hit-rate 20 -w examples/sample_wordlist.txt example.com
Press Ctrl+C at any time to abort. In-flight queries drain and partial results are flushed before exit.
Interactive TUI
./subenum -tui
No flags required. Fill in the form and press ctrl+r to start scanning. Last-used values are saved to ~/.config/subenum/last.json and restored on next launch. The interface is shown at the top of this README.
TUI keyboard reference
| Key | Action |
|---|---|
tab / shift+tab / ↑ ↓ |
Navigate fields |
space |
Toggle Simulate / Force |
ctrl+r |
Start scan |
ctrl+c |
Abort scan (scan view) / quit (form) |
r |
New scan - restores last-used values |
q |
Quit after scan completes |
Tech Stack
| Layer | Components |
|---|---|
| Core Engine | Go 1.24.2 · net.Resolver · context · sync/atomic |
| Concurrency | goroutines · channels · sync.WaitGroup · sync.Mutex |
| TUI | Bubble Tea · Bubbles (textinput, viewport, progress) · Lip Gloss |
| Infrastructure | Docker · Alpine · Make · docker-compose |
| CI/CD | GitHub Actions · CodeQL · Dependabot · golangci-lint v2 |
| Quality | go test -race · gosec · govet · staticcheck |
Project structure
subenum/
├── .github/
│ ├── workflows/
│ │ ├── go.yml # CI: build, test, lint, release
│ │ ├── codeql.yml # Weekly CodeQL security analysis
│ │ └── pages.yml # GitHub Pages deployment
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── PULL_REQUEST_TEMPLATE.md
├── data/
│ └── wordlist.txt # Default wordlist for Docker/Make
├── docs/
│ ├── assets/
│ │ └── tui-form.png # TUI screenshot
│ ├── ARCHITECTURE.md
│ ├── CONTRIBUTING.md
│ ├── DEVELOPER_GUIDE.md
│ ├── docker.md
│ ├── _config.yml
│ └── index.md
├── examples/
│ ├── sample_wordlist.txt
│ ├── advanced_usage.md
│ ├── demo.sh
│ └── multi_domain_scan.sh
├── internal/
│ ├── dns/ # ResolveDomain, CheckWildcard, SimulateResolution
│ ├── output/ # Thread-safe Writer (stdout/stderr separation)
│ ├── scan/ # Scan engine: Config, Event types, Run()
│ ├── tui/ # Bubble Tea UI: form, scan view, session config
│ └── wordlist/ # LoadWordlist (dedup + sanitize)
├── tools/
│ └── wordlist-gen.go
├── main.go # CLI entry point
├── main_test.go
├── go.mod
├── Dockerfile
├── docker-compose.yml
├── Makefile
├── .golangci.yml # golangci-lint v2 configuration
├── CHANGELOG.md
├── SECURITY.md
└── LICENSE # GNU General Public License v3.0
Development
See CONTRIBUTING.md for the pull request workflow and ethical guidelines. See DEVELOPER_GUIDE.md for build setup, testing, and project structure.
Documentation
¶
There is no documentation for this package.