All checks were successful
check / check (push) Successful in 53s
DNS is never mocked in this repository: tests exercise live DNS, and robustness comes from handling real-world DNS behavior with tolerant assertions and sensible timeouts, not from mocks. watcher: drop mockResolver and wire the real iterative resolver into the tests, querying stable public names (example.com, www.example.com). Change detection is exercised by seeding the state store with a synthetic previous observation that live DNS cannot match (reserved .invalid nameserver names and RFC 5737 documentation addresses); DNS stays live in every run. The port checker, TLS checker, and notifier remain test doubles since they are not DNS, keeping notification and state assertions deterministic against whatever addresses live DNS returns. resolver: drop the timeoutClient fake DNSClient and the NewFromLoggerWithClient mock constructor. The timeout test is replaced by a live query against an RFC 5737 documentation address where no nameserver can exist, asserting a classified non-OK response with no records. TESTING.md: extend the live-DNS policy to every package and remove the carve-out that permitted DNS mocks in packages that consume the resolver. TODO.md: update stale references to hermetic mocked-DNS work to reflect the no-mocking policy and the current state of feature/resolver. Intentionally dropped coverage: the exact StatusTimeout classification (previously forced by the fake client) is no longer asserted, because a genuinely unreachable server may fail fast instead of timing out depending on the network path; the live test tolerantly accepts any failure classification.
164 lines
7.7 KiB
Markdown
164 lines
7.7 KiB
Markdown
# Workflow
|
|
|
|
* branch (from `main`)
|
|
* do the work in Next Step
|
|
* move Next Step to the top of Completed Steps
|
|
* move the top item of Future Steps into Next Step
|
|
* commit (`TODO.md` changes in the same commit as the work)
|
|
* merge to `main` if the branch is not protected, otherwise open a PR
|
|
* push
|
|
|
|
# Status
|
|
|
|
pre-1.0. No git tags. Core resolver work in flight on feature/resolver
|
|
(dirty: internal/resolver/resolver_test.go). Local checkout has diverged
|
|
from origin: origin/main is 8 commits ahead (watcher orchestrator,
|
|
unified TARGETS) and origin/feature/resolver already contains the full
|
|
iterative resolver implementation. DNS mocking is banned in this repo
|
|
(see `TESTING.md`): all tests use live DNS only. The hermetic mocked
|
|
tests previously noted on `feature/resolver` are gone from its current
|
|
tip, which carries a live-DNS suite against `*.dns.sneak.cloud`.
|
|
|
|
# Next Step
|
|
|
|
Policy scaffold commit: add LICENSE, REPO_POLICIES.md, .editorconfig,
|
|
.dockerignore, and .gitea/workflows/check.yml, and add the missing
|
|
fmt-check, docker, and hooks targets to the Makefile. One commit, then
|
|
confirm make check still passes.
|
|
|
|
# Completed Steps
|
|
|
|
- 2026-08-07: DNS mocking removed from the entire test suite; watcher
|
|
tests now drive the real iterative resolver against live DNS and
|
|
`TESTING.md` bans DNS mocks in every package (`remove-dns-mocking`
|
|
branch)
|
|
- 2026-08-07: golangci-lint bumped to v2.12.2 (commit-pinned installs
|
|
in `Dockerfile` and `script/bootstrap`); `.golangci.yml` set to the
|
|
org-standard v2-schema config used across the org's repos
|
|
(owner-authorized; same file is being landed as canonical via prompts
|
|
PR #24), with settings under `linters.settings` so the
|
|
lll/funlen/cyclop/dupl thresholds apply; fixed the resulting
|
|
`goconst`, `dupl`, and `lll` findings; the informational `gomodguard`
|
|
deprecation warning under this config is accepted
|
|
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints,
|
|
Makefile shims, README Entrypoints section
|
|
- 2026-02-20: iterative DNS resolver implemented; tests made hermetic
|
|
with mocked DNS (origin/feature/resolver, unmerged; superseded — DNS
|
|
mocking is banned, see `TESTING.md`)
|
|
- 2026-02-20: CI actions and go install refs pinned to commit SHAs;
|
|
Gitea Actions workflow for make check (origin/ci/make-check, unmerged)
|
|
- 2026-02-20: watcher monitoring orchestrator merged to main (#8)
|
|
- 2026-02-20: DOMAINS/HOSTNAMES unified into single TARGETS config (#11)
|
|
- 2026-02-19: TCP port connectivity checker, made concurrent with port
|
|
validation; gosec G704 SSRF findings fixed without suppression
|
|
(feature branches, unmerged)
|
|
- 2026-02-19: TLS certificate inspector with no-peer-certificates error
|
|
path and IP SANs (feature branch, unmerged)
|
|
- 2026-02-19: gosec SSRF and formatting fixes on main
|
|
- 2026-02-19: initial scaffold with per-nameserver DNS monitoring model
|
|
|
|
# Future Steps
|
|
|
|
Compliance:
|
|
|
|
- Add README sections required by policy (Description, Getting Started,
|
|
Rationale, Design, TODO, License, Author) if any are missing
|
|
- Pin Dockerfile base images by sha256 and ensure the Docker build runs
|
|
make check
|
|
|
|
Branch reconciliation:
|
|
|
|
- Sync local checkout with origin: local main is 8 commits behind
|
|
origin/main; local feature/resolver has diverged from
|
|
origin/feature/resolver, which already implements the resolver
|
|
- Merge in-flight branches to main once green: feature/resolver
|
|
(confirm its tests remain live-DNS — DNS mocking is banned, see
|
|
`TESTING.md`), ci/make-check, feature/portcheck-implementation,
|
|
feature/tlscheck-implementation
|
|
|
|
Resolver (plan from untracked TODO.md; largely implemented on
|
|
origin/feature/resolver, verify each item before closing):
|
|
|
|
- Add github.com/miekg/dns dependency
|
|
- roots.go: hardcoded IANA root server list (a through m, IPv4/IPv6),
|
|
rootServers() returning ip:53 strings
|
|
- query.go: low-level query(ctx, server, name, qtype): UDP with TCP
|
|
fallback on truncation, RD=0, context respected, 5s per-query timeout,
|
|
returns raw *dns.Msg
|
|
- trace.go: iterative delegation chasing from roots: referral detection
|
|
(NOERROR, empty answer, NS in authority), glue extraction with
|
|
bailiwick check, out-of-bailiwick NS resolved with recursion guard,
|
|
delegation depth limit (20), retry across nameservers on failure, do
|
|
not chase CNAMEs inside trace
|
|
- FindAuthoritativeNameservers: NS set via trace, sorted, FQDN
|
|
normalized, trailing dot handled; must pass its 9 tests
|
|
- QueryNameserver: resolve NS host to IPs, query A/AAAA/CNAME/MX/TXT/
|
|
SRV/CAA/NS, build NameserverResponse with status mapping (OK,
|
|
NXDomain, NoData, Error), documented record formatting, sorted values,
|
|
lame delegation detection; must pass its 16 tests
|
|
- QueryAllNameservers: find NS set for parent domain (public suffix
|
|
list), query all NS in parallel with bounded concurrency, return map
|
|
even when all fail, context cancellation; must pass its 4 tests
|
|
- LookupNS: thin wrapper over FindAuthoritativeNameservers, sorted,
|
|
identical results; must pass its 3 tests
|
|
- ResolveIPAddresses: collect A/AAAA from all NS, follow CNAME chains
|
|
with MaxCNAMEDepth, dedupe, sort, NXDOMAIN returns empty slice with
|
|
nil error; must pass its 9 tests
|
|
- All 39 resolver tests pass, make check green, merge to main
|
|
|
|
Watcher (internal/watcher/watcher.go):
|
|
|
|
- Scheduling loop in Run(ctx): initial check on startup, separate
|
|
tickers for DNS/port and TLS intervals, persist state via state.Save()
|
|
after each cycle, clean shutdown on context cancel
|
|
- Domain check: LookupNS, compare to stored state, store silently on
|
|
first run, notify with old/new NS lists on change
|
|
- Hostname check: QueryAllNameservers, compare per-NS records; notify on
|
|
record changes, NS failure, NS recovery, inconsistency detected,
|
|
inconsistency resolved, empty response; store silently on first run
|
|
- Port check: ResolveIPAddresses, check ports 80 and 443 per IP, notify
|
|
on open/closed transitions, handle new and disappeared IPs
|
|
- TLS check: for each open IP:443, CheckCertificate; notify on expiry
|
|
warning, certificate change (CN/issuer/SANs), TLS failure/recovery
|
|
|
|
Port checker (internal/portcheck/portcheck.go):
|
|
|
|
- Tests against known-open ports and RFC documentation IPs
|
|
- CheckPort: net.DialTimeout (5s), context respected; (true, nil) open,
|
|
(false, nil) closed/timeout/refused, error only for unexpected
|
|
failures
|
|
|
|
TLS checker (internal/tlscheck/tlscheck.go):
|
|
|
|
- Tests against known public HTTPS servers, verify fields populated
|
|
- CheckCertificate: tls.Dial to specific IP:443 with hostname as SNI;
|
|
extract subject CN, issuer CN and org, NotAfter, SANs; error on
|
|
handshake failure
|
|
|
|
Notification service (internal/notify/notify.go, Slack/Mattermost/ntfy
|
|
backends exist):
|
|
|
|
- Structured notification types: DNS change, port change, TLS expiry,
|
|
TLS change, NS failure, NS recovery, NS inconsistency
|
|
- Per-backend formatting: Slack/Mattermost attachment colors (red
|
|
failures/expiry, yellow warnings, green recoveries, blue info); ntfy
|
|
priorities (urgent failures, high warnings, default changes, low
|
|
recoveries); include hostname, nameserver, old/new values, timestamps
|
|
|
|
HTTP API handlers:
|
|
|
|
- Wire *state.State and *watcher.Watcher into handler params
|
|
- GET /api/v1/status: full state snapshot as JSON
|
|
- GET /api/v1/domains: domain states with NS records and last-checked
|
|
- GET /api/v1/hostnames: hostname states with per-NS record data
|
|
|
|
Infrastructure notes (from untracked TODO.md):
|
|
|
|
- Module path sneak.berlin/go/dnswatcher differs from the git.eeqj.de
|
|
remote intentionally; do not "fix" it
|
|
- Dependencies: github.com/miekg/dns, golang.org/x/net/publicsuffix
|
|
- Resolver tests originally used live DNS against `*.dns.sneak.cloud`
|
|
(required records documented in the test file header); `main` now
|
|
tests against live public DNS. DNS mocking is banned (see
|
|
`TESTING.md`); never reintroduce hermetic mocked DNS tests
|