- runner.go: store fingerprints in local var, call formatter.SetFingerprints()
instead of mutating caller's *Config; removes Fingerprints field from Config
- fingerprint.go: use singleflight.Group in Query to coalesce concurrent
probes for the same IP, eliminating the double-probe race
- output layer: add SetFingerprints(map[string]string) to Formatter interface;
textFormatter and jsonFormatter each hold their own fingerprints field
- formatter_test.go: add TestTextFormatterWriteSummaryShowsVersions and
TestJSONFormatterWriteSummaryShowsVersions verifying version output
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>
- Add internal/fingerprint package with Fingerprinter type
- Sends version.bind CHAOS TXT query to each server
- Caches results per IP to avoid redundant queries
- Concurrent batch fingerprinting via FingerprintAll
- Gracefully handles non-responding servers (returns empty string)
- Injectable exchange function for testability
- Wire fingerprinting into RunTraversal
- Triggered when both ShowVersions and ShowServers are enabled
- Collects unique server IPs from traversal results
- Stores results in output.Config.Fingerprints before WriteSummary
- Display versions in text output (writeServers)
- Appends version string after IP when ShowVersions is true
- No output change when version is unknown
- Include version in JSON output (jsonServer.Version field)
- omitempty: field absent when version is unknown
- Add comprehensive fingerprint tests (11 test cases)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: multica-agent <github@multica.ai>