feat: implement watcher monitoring orchestrator (closes #2) #8

Merged
sneak merged 4 commits from feature/watcher-implementation into main 2026-02-20 09:06:42 +01:00
Collaborator

Summary

Implements the full watcher monitoring orchestrator as described in the README.

Changes

  • Interfaces (interfaces.go): DNSResolver, PortChecker, TLSChecker, Notifier — enables testing with mocks and future implementation swaps
  • Watcher implementation (watcher.go): Full monitoring loop with:
    • Immediate checks on startup + periodic DNS/port and TLS cycles
    • Domain NS change detection
    • Per-nameserver hostname record tracking (changes, failures, recoveries, inconsistencies)
    • TCP port 80/443 monitoring
    • TLS cert monitoring (changes, 7-day expiry warnings, failures/recoveries)
    • State persistence after each cycle
    • First-run baseline (no notifications)
    • Graceful shutdown
  • fx wiring (main.go): Interface adapters for concrete types
  • Resolver stub (resolver.go): Updated LookupAllRecords signature to return per-NS results
  • State helper (state_test_helper.go): NewForTest() for unit testing

Test Results

=== RUN   TestFirstRunBaseline       --- PASS
=== RUN   TestNSChangeDetection      --- PASS
=== RUN   TestRecordChangeDetection  --- PASS
=== RUN   TestPortStateChange        --- PASS
=== RUN   TestTLSExpiryWarning       --- PASS
=== RUN   TestGracefulShutdown       --- PASS
=== RUN   TestNSFailureAndRecovery   --- PASS
PASS ok sneak.berlin/go/dnswatcher/internal/watcher 1.455s

Lint

All new code passes linting. Only pre-existing gosec warnings in notify.go remain (SSRF taint analysis on webhook URLs from config).

Closes #2

## Summary Implements the full watcher monitoring orchestrator as described in the README. ## Changes - **Interfaces** (`interfaces.go`): `DNSResolver`, `PortChecker`, `TLSChecker`, `Notifier` — enables testing with mocks and future implementation swaps - **Watcher implementation** (`watcher.go`): Full monitoring loop with: - Immediate checks on startup + periodic DNS/port and TLS cycles - Domain NS change detection - Per-nameserver hostname record tracking (changes, failures, recoveries, inconsistencies) - TCP port 80/443 monitoring - TLS cert monitoring (changes, 7-day expiry warnings, failures/recoveries) - State persistence after each cycle - First-run baseline (no notifications) - Graceful shutdown - **fx wiring** (`main.go`): Interface adapters for concrete types - **Resolver stub** (`resolver.go`): Updated `LookupAllRecords` signature to return per-NS results - **State helper** (`state_test_helper.go`): `NewForTest()` for unit testing ## Test Results ``` === RUN TestFirstRunBaseline --- PASS === RUN TestNSChangeDetection --- PASS === RUN TestRecordChangeDetection --- PASS === RUN TestPortStateChange --- PASS === RUN TestTLSExpiryWarning --- PASS === RUN TestGracefulShutdown --- PASS === RUN TestNSFailureAndRecovery --- PASS PASS ok sneak.berlin/go/dnswatcher/internal/watcher 1.455s ``` ## Lint All new code passes linting. Only pre-existing gosec warnings in `notify.go` remain (SSRF taint analysis on webhook URLs from config). Closes #2
sneak was assigned by clawbot 2026-02-19 22:49:06 +01:00
clawbot added 2 commits 2026-02-19 22:49:06 +01:00
Tests cover: first-run baseline, NS change detection, record change
detection, port state changes, TLS expiry warnings, graceful shutdown,
and NS failure/recovery scenarios.
Implements the full monitoring loop:
- Immediate checks on startup, then periodic DNS+port and TLS cycles
- Domain NS change detection with notifications
- Per-nameserver hostname record tracking with change/failure/recovery
  and inconsistency detection
- TCP port 80/443 monitoring with state change notifications
- TLS certificate monitoring with change, expiry, and failure detection
- State persistence after each cycle
- First run establishes baseline without notifications
- Graceful shutdown via context cancellation

Defines DNSResolver, PortChecker, TLSChecker, and Notifier interfaces
for dependency injection. Updates main.go fx wiring and resolver stub
signature to match per-NS record format.

Closes #2
Owner

@clawbot does LookupAllRecords get all RR types or just address records? it should get all RR types incl TXT etc.

@clawbot does LookupAllRecords get all RR types or just address records? it should get all RR types incl TXT etc.
Author
Collaborator

The interface and signature are designed for all RR types — the return type is map[nameserver]map[recordType][]values, and the interface doc says "queries all record types for a hostname." However, the actual implementation in internal/resolver/resolver.go is currently a stub that returns ErrNotImplemented.

So the intent is all RR types (A, AAAA, TXT, MX, CNAME, etc.), but whether it actually delivers on that depends on the future implementation. The contract is correct as written.

The interface and signature are designed for all RR types — the return type is `map[nameserver]map[recordType][]values`, and the interface doc says "queries all record types for a hostname." However, the actual implementation in `internal/resolver/resolver.go` is currently a stub that returns `ErrNotImplemented`. So the *intent* is all RR types (A, AAAA, TXT, MX, CNAME, etc.), but whether it actually delivers on that depends on the future implementation. The contract is correct as written.
clawbot added 1 commit 2026-02-20 08:42:52 +01:00
Validate webhook/ntfy URLs at Service construction time and add
targeted nolint directives for pre-validated URL usage.
Author
Collaborator

make check audit result

All checks passed.

==> Checking formatting...
==> Running linter...
0 issues.
==> Running tests...
--- PASS: TestNSChangeDetection (0.00s)
--- PASS: TestFirstRunBaseline (0.00s)
--- PASS: TestPortStateChange (0.00s)
--- PASS: TestTLSExpiryWarning (0.00s)
--- PASS: TestRecordChangeDetection (0.00s)
--- PASS: TestNSFailureAndRecovery (0.00s)
--- PASS: TestGracefulShutdown (0.25s)
PASS
==> Building...
==> All checks passed!
## `make check` audit result ✅ All checks passed. ``` ==> Checking formatting... ==> Running linter... 0 issues. ==> Running tests... --- PASS: TestNSChangeDetection (0.00s) --- PASS: TestFirstRunBaseline (0.00s) --- PASS: TestPortStateChange (0.00s) --- PASS: TestTLSExpiryWarning (0.00s) --- PASS: TestRecordChangeDetection (0.00s) --- PASS: TestNSFailureAndRecovery (0.00s) --- PASS: TestGracefulShutdown (0.25s) PASS ==> Building... ==> All checks passed! ```
Author
Collaborator

make check passes

==> Checking formatting...
==> Running linter...
==> Running tests...
PASS ok sneak.berlin/go/dnswatcher/internal/watcher 1.588s
==> Building...
==> All checks passed!
make check passes ✅ ``` ==> Checking formatting... ==> Running linter... ==> Running tests... PASS ok sneak.berlin/go/dnswatcher/internal/watcher 1.588s ==> Building... ==> All checks passed! ```
sneak added 1 commit 2026-02-20 09:06:29 +01:00
sneak merged commit e185000402 into main 2026-02-20 09:06:42 +01:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sneak/dnswatcher#8
No description provided.