All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Structured output (
-format jsonand-format csv) is now finalized only on the successful scan path. The finalizer previously ran viadeferon every exit, so an early error (such as wildcard detection without-force) emitted an empty JSON array. Text output behavior is unchanged.
- GitHub Pages landing page (
docs/index.md) refreshed to the 0.6.0 feature set, adding cards for Output Formats (-format), Rate Limiting (-rate), Record Types (-type), and Recursive Enumeration (-recursive/-depth). - Normalized em dashes to hyphens across
docs/for consistency with the no-em-dash convention.
- Resolved records are now captured during scans.
internal/dnsexposesResolveand aRecord{Type, Value}type;scan.EventcarriesRecordsfor each resolved subdomain (A/AAAA today, extensible to CNAME and more). -format text|json|csvflag (defaulttext, byte-for-byte identical to prior output). JSON emits a buffered array of{"subdomain", "records"}objects; CSV streamssubdomain,type,valuerows with a header. The-ooutput file honors the selected format. Output formats are CLI-only for now (TUI-pending).-rate <qps>flag (default 0 = unlimited) caps total DNS queries per second across the worker pool via a shared stdlib ticker gate insidescan.Run. The limiter respects context cancellation soCtrl+Cstays responsive.-type A,AAAA,CNAMEflag (defaultA,AAAA, preserving prior behavior) performs per-type DNS lookups and filters results to the requested types. The resolved record type is carried in the existingRecordshape, so the JSON/CSV schema is unchanged.-recursiveand-depth <n>flags for recursive enumeration of discovered subdomains.scan.Runwas restructured around a dispatcher that tracks outstanding work and closes the queue only when it drains to zero, so resolved subdomains can safely enqueue depth-capped children (the previous close-after-feed shape would have panicked on a send to a closed channel). A centralized visited set provides loop and duplicate protection, and the progress total expands as new work is discovered.
- Internal: the scan engine's worker queue lifecycle moved from a feed-then-close channel to a dispatcher-owned queue with a pending-work counter.
- Data race in simulation mode: migrated
internal/dns/simulate.gotomath/rand/v2, whose top-level functions are goroutine-safe and auto-seeded.SimulateResolutionis now safe to call concurrently (previously a shared*math/rand.Randwas used from every worker). - Send-on-closed-channel race in
scan.Run: the progress ticker goroutine now signals its own exit (tickerStopped) and guards its send with a select, andRunwaits for that exit before emittingEventDone, so the deferredclose(events)can no longer race an in-flight ticker send. - TUI now renders the "Aborted" status when a scan is cancelled with
ctrl+c(the scan view is marked aborted so the subsequentEventDoneshows partial counts). - TUI form no longer blocks a live-mode scan when the Hit Rate field is empty or out of range; hit rate is validated only when Simulate is on.
-versionnow reports the correct version (subenum v0.5.1).- Docker build: the builder now copies
go.sumand runsgo mod downloadbefore building, the base image satisfies the module Go version, andmain_test.gois no longer copied into the build image.
- Go minimum version reconciled to 1.24.2 across
go.mod, the Dockerfile base image, README, and docs (the charmbracelet TUI dependencies require it); direct vs indirect dependency classification corrected viago mod tidy.
- Tests: concurrent
SimulateResolutiontest,internal/scanrunner tests (concurrent simulate run and mid-scan context cancellation), andinternal/tuiform validation tests.
- README facelift: plain-text description under the badges, a copy-paste quick-start block, the TUI screenshot promoted to a hero position, PRs-Welcome and platform badges, and removal of em dashes for a clean human-authored look.
- ARCHITECTURE: corrected the progress ticker interval (1 second) and the argument-parsing section (
flag.*Varinto acliFlagsstruct). - Removed the unused, duplicate
docs/assets/title.svg.
- Interactive terminal UI (
-tuiflag) built with Bubble Tea — form-based config screen and live-scrolling results view; no CLI arguments required to launch make tuiMakefile target for one-command TUI launchinternal/scanpackage: extracted scan engine (scan.Run) with typedEventchannel, usable by both CLI and TUI- TUI session persistence: last-used form values written to
~/.config/subenum/last.jsonand restored on next launch or after pressingr(new scan)
- CLI scan loop in
main.gonow delegates toscan.Run()instead of containing the worker pool inline - External dependencies added:
github.com/charmbracelet/bubbleteaandgithub.com/charmbracelet/bubbles(TUI only; CLI path has zero external dependencies) - TUI form field order: Simulate toggle promoted to field 3 (was field 8); Hit Rate row is hidden when Simulate is OFF
- TUI now shows a blinking cursor inside the active text input
- Pressing
ron the scan results screen returns to the form with last-used values pre-filled (was reset to defaults)
- Wildcard DNS detection with double-probe confirmation; exits by default, continue with
-force - Wordlist deduplication (duplicates removed before scanning, count reported in verbose mode)
-attemptsflag replacing-retries(deprecated, still accepted with warning)
- Refactored into
internal/dns,internal/output,internal/wordlistpackages - Progress, verbose, and diagnostic output moved to stderr (stdout is now pipe-clean)
- Version bumped to 0.4.0
- Progress ticker no longer corrupts piped stdout output
-retriessemantics clarified via rename to-attempts
- Output file support with the
-oflag to save results to a file - DNS retry mechanism with configurable
-retriesflag for transient failure resilience - Graceful shutdown on SIGINT/SIGTERM — drains in-flight workers and prints partial results
- Proper DNS server validation (IP format and port range 1-65535)
- Domain format validation against DNS naming rules
- Tests for
validateDNSServer,validateDomain,resolveDomainWithRetry, andsimulateResolution
- Removed deprecated
rand.Seedcall (auto-seeded since Go 1.20) - Tests now use
t.Errorffor real assertions instead oft.Logfwarnings - Fixed test compilation —
resolveDomaincalls now pass all 4 required parameters - Updated all placeholder URLs/emails in documentation to actual repo values
- Progress goroutine
donechannel is now buffered to prevent potential deadlock - Mutex-protected stdout/file output to prevent interleaved writes from concurrent workers
- Custom DNS server support with the
-dns-serverflag - Verbose output mode with the
-vflag - Progress reporting during scans (enabled by default)
- Version information accessible via the
-versionflag - Input validation for concurrency and timeout values
- Legal disclaimers and usage restrictions to prevent misuse
- Comprehensive documentation via README.md and docs folder
- Developer Guide with setup and contribution instructions
- Example wordlist and multi-domain scanning script
- Basic test suite for DNS resolution
- Enhanced error handling with user-friendly messages
- Improved code structure and documentation
- DNS resolution now reports timing information in verbose mode
- Proper cleanup of resources after scans complete
- Prevention of negative values for concurrency and timeout
- Initial project setup with basic functionality
- Concurrent subdomain enumeration using goroutines
- DNS resolution with configurable timeout
- Command-line flags for wordlist, concurrency, and timeout