When DNSWATCHER_TARGETS is empty (the default), dnswatcher previously started successfully and ran indefinitely monitoring nothing. This is a common misconfiguration — forgetting to set the variable or making a typo in its name — and gave no indication anything was wrong.
Changes
Added ErrNoTargets sentinel error in internal/config/config.go
Extracted parseAndValidateTargets() helper to validate that at least one domain or hostname is configured after target classification
If no targets are configured, dnswatcher now exits with a clear error: "no monitoring targets configured: set DNSWATCHER_TARGETS environment variable"
Updated README.md to document that DNSWATCHER_TARGETS is required and dnswatcher will refuse to start without it
How it works
The validation runs during config construction (via uber/fx), before the watcher or any other component starts. If DNSWATCHER_TARGETS is empty or contains only whitespace/empty entries, buildConfig() returns ErrNoTargets, which causes fx to fail startup with a clear error message.
This is fail-fast behavior: a monitoring daemon with nothing to monitor is a misconfiguration and should not silently run.
## Summary
When `DNSWATCHER_TARGETS` is empty (the default), dnswatcher previously started successfully and ran indefinitely monitoring nothing. This is a common misconfiguration — forgetting to set the variable or making a typo in its name — and gave no indication anything was wrong.
## Changes
- Added `ErrNoTargets` sentinel error in `internal/config/config.go`
- Extracted `parseAndValidateTargets()` helper to validate that at least one domain or hostname is configured after target classification
- If no targets are configured, dnswatcher now exits with a clear error: `"no monitoring targets configured: set DNSWATCHER_TARGETS environment variable"`
- Updated README.md to document that `DNSWATCHER_TARGETS` is required and dnswatcher will refuse to start without it
## How it works
The validation runs during config construction (via uber/fx), before the watcher or any other component starts. If `DNSWATCHER_TARGETS` is empty or contains only whitespace/empty entries, `buildConfig()` returns `ErrNoTargets`, which causes fx to fail startup with a clear error message.
This is fail-fast behavior: a monitoring daemon with nothing to monitor is a misconfiguration and should not silently run.
Closes https://git.eeqj.de/sneak/dnswatcher/issues/69
Validation runs during config construction (via uber/fx) before watcher starts — true fail-fast ✅
Edge cases handled: empty string, whitespace-only, and empty CSV entries are all stripped by parseCSV() before reaching validation ✅
README.md (+6/-0)
Documents that DNSWATCHER_TARGETS is required and dnswatcher will refuse to start without it ✅
Verification
docker build . — PASS (all tests green)
No test files modified — no test weakening ✅
Existing behavior unchanged when targets ARE configured ✅
One clean commit, properly scoped
Notes
No unit test added for parseAndValidateTargets(), but acceptable: it is unexported, the validation is a trivial length check, and ClassifyTargets is already well-tested in classify_test.go.
Observed a transient flake in TestQueryNameserver_TXT (live DNS — google.com TXT returned empty from one nameserver). Unrelated to this PR; passed on retry. Pre-existing issue tracked separately.
## Review: ✅ PASS
### Summary
Clean, minimal implementation of fail-fast validation when `DNSWATCHER_TARGETS` is empty. Correctly addresses issue #69.
### Code Review
**`internal/config/config.go`** (+26/-4)
- `ErrNoTargets` sentinel error: exported, descriptive message, good practice ✅
- `parseAndValidateTargets()` helper: cleanly extracted from `buildConfig()`, preserves the existing `ClassifyTargets` error wrapping, adds `len(domains) == 0 && len(hostnames) == 0` check ✅
- Validation runs during config construction (via uber/fx) before watcher starts — true fail-fast ✅
- Edge cases handled: empty string, whitespace-only, and empty CSV entries are all stripped by `parseCSV()` before reaching validation ✅
**`README.md`** (+6/-0)
- Documents that `DNSWATCHER_TARGETS` is required and dnswatcher will refuse to start without it ✅
### Verification
- `docker build .` — **PASS** (all tests green)
- No test files modified — no test weakening ✅
- Existing behavior unchanged when targets ARE configured ✅
- One clean commit, properly scoped
### Notes
- No unit test added for `parseAndValidateTargets()`, but acceptable: it is unexported, the validation is a trivial length check, and `ClassifyTargets` is already well-tested in `classify_test.go`.
- Observed a transient flake in `TestQueryNameserver_TXT` (live DNS — google.com TXT returned empty from one nameserver). Unrelated to this PR; passed on retry. Pre-existing issue tracked separately.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
When
DNSWATCHER_TARGETSis empty (the default), dnswatcher previously started successfully and ran indefinitely monitoring nothing. This is a common misconfiguration — forgetting to set the variable or making a typo in its name — and gave no indication anything was wrong.Changes
ErrNoTargetssentinel error ininternal/config/config.goparseAndValidateTargets()helper to validate that at least one domain or hostname is configured after target classification"no monitoring targets configured: set DNSWATCHER_TARGETS environment variable"DNSWATCHER_TARGETSis required and dnswatcher will refuse to start without itHow it works
The validation runs during config construction (via uber/fx), before the watcher or any other component starts. If
DNSWATCHER_TARGETSis empty or contains only whitespace/empty entries,buildConfig()returnsErrNoTargets, which causes fx to fail startup with a clear error message.This is fail-fast behavior: a monitoring daemon with nothing to monitor is a misconfiguration and should not silently run.
Closes #69
Review: ✅ PASS
Summary
Clean, minimal implementation of fail-fast validation when
DNSWATCHER_TARGETSis empty. Correctly addresses issue #69.Code Review
internal/config/config.go(+26/-4)ErrNoTargetssentinel error: exported, descriptive message, good practice ✅parseAndValidateTargets()helper: cleanly extracted frombuildConfig(), preserves the existingClassifyTargetserror wrapping, addslen(domains) == 0 && len(hostnames) == 0check ✅parseCSV()before reaching validation ✅README.md(+6/-0)DNSWATCHER_TARGETSis required and dnswatcher will refuse to start without it ✅Verification
docker build .— PASS (all tests green)Notes
parseAndValidateTargets(), but acceptable: it is unexported, the validation is a trivial length check, andClassifyTargetsis already well-tested inclassify_test.go.TestQueryNameserver_TXT(live DNS — google.com TXT returned empty from one nameserver). Unrelated to this PR; passed on retry. Pre-existing issue tracked separately.