LookupNS returns only a domain's own delegation. When its parent zone's servers answer NXDOMAIN, it returns the new ErrNXDomain; the watcher then saves the domain with no nameservers and nxdomain set, asks for none of its records and removes those saved earlier. The dashboard shows "does not exist" in its Nameservers cell, and /api/v1/status gives "nxdomain": true. Its old nameservers go in one NS Change with all of them removed.
A domain with no delegation of its own, such as octocat.github.io, gets an empty set without nxdomain, and its records are still asked at the servers of the zone it is in.
FindAuthoritativeNameservers, used for hostnames, moves to a parent name only when the servers asked answer that the name has no delegation of its own or does not exist. When they do not answer, it returns the error and the hostname's saved records are kept. The fallback walk, resolveNSIterative, reads answers the same way.
README updated to match.
Not in the diff:
After the upgrade, a domain without its own delegation that an earlier version saved with its parent zone's nameservers gets one NS Change with all of them removed on its first check. The README says so under the NS change notification.
Tests that start a walk at 192.0.2.1 use a resolver whose UDP queries give up after 100 ms.
Disclosures:
Judgement call: a domain that stops existing loses its saved records without a notification of their own; the NS Change reports it.
Model: opus-5-5
Closes https://git.eeqj.de/sneak/dnswatcher/issues/222
- `LookupNS` returns only a domain's own delegation. When its parent zone's servers answer NXDOMAIN, it returns the new `ErrNXDomain`; the watcher then saves the domain with no nameservers and `nxdomain` set, asks for none of its records and removes those saved earlier. The dashboard shows "does not exist" in its Nameservers cell, and `/api/v1/status` gives `"nxdomain": true`. Its old nameservers go in one NS Change with all of them removed.
- A domain with no delegation of its own, such as `octocat.github.io`, gets an empty set without `nxdomain`, and its records are still asked at the servers of the zone it is in.
- `FindAuthoritativeNameservers`, used for hostnames, moves to a parent name only when the servers asked answer that the name has no delegation of its own or does not exist. When they do not answer, it returns the error and the hostname's saved records are kept. The fallback walk, `resolveNSIterative`, reads answers the same way.
- README updated to match.
Not in the diff:
- After the upgrade, a domain without its own delegation that an earlier version saved with its parent zone's nameservers gets one NS Change with all of them removed on its first check. The README says so under the NS change notification.
- Tests that start a walk at 192.0.2.1 use a resolver whose UDP queries give up after 100 ms.
Disclosures:
- Judgement call: a domain that stops existing loses its saved records without a notification of their own; the NS Change reports it.
Model: opus-5-5
A .com domain that does not exist still shows the .com servers, and nothing says it does not exist. checkDomain in internal/watcher/watcher.go still asks the domain's own records at the servers of the zone it is in. So the dashboard's second Domains table, and recordsByNameserver in /api/v1/status, list all 13 .com servers against the domain under NS, each now ok with no records. Its Nameservers cell is blank, the same as for a domain that exists but has no delegation of its own (such as octocat.github.io). The reproduction in #222 still shows the complaint it describes. Acceptable: a domain whose parent zone's servers answer that it does not exist is shown as not existing on the dashboard and in /api/v1/status, with no parent zone's servers listed against it. A domain that exists without a delegation of its own still has its records watched.
No test checks that a walk that got no answer is an error and not "no delegation of its own" (internal/resolver/resolver_test.go). The PR says this needs a stand-in, but it does not: followDelegation already takes the servers it starts from, and tests such as TestQueryServers_NotEveryRootServerRefused give the real resolver 192.0.2.1, where nothing answers. Without such a test, the suite stays green if a walk with no answer turns into an empty set, which sends an NS Change with every nameserver removed. It also stays green if FindAuthoritativeNameservers goes back to trying a parent name after a failed walk. Acceptable: live tests that start the walk at 192.0.2.1 and check two things: the walk LookupNS uses returns an error, not an empty set; and FindAuthoritativeNameservers returns that error without trying a parent name.
No test reaches the new branch in resolveNSIterative (internal/resolver/iterative.go) that reads an authoritative reply as "no delegation of its own". Acceptable: a live test of that walk on a name with no delegation of its own (for example www.google.com) that expects an empty set and no error, and fails without the branch.
The NS Change sent after an upgrade is described only in the PR body. Take a name the public suffix list makes a domain but that has no delegation of its own (such as octocat.github.io), saved by an earlier version with its parent zone's nameservers. On its first check it gets a warning NS Change with all of them removed, though it still exists. The README mentions an NS Change with every nameserver removed only as meaning that the domain no longer exists. Acceptable: README.md describes this where an operator reads it, next to the NS Change bullet in "DNS Domain Monitoring" or with the other upgrade notes under "State File Format", and says that it does not mean the domain stopped existing.
Model: opus-5-5
Review failed on `65b3212`.
1. A `.com` domain that does not exist still shows the `.com` servers, and nothing says it does not exist. `checkDomain` in `internal/watcher/watcher.go` still asks the domain's own records at the servers of the zone it is in. So the dashboard's second Domains table, and `recordsByNameserver` in `/api/v1/status`, list all 13 `.com` servers against the domain under NS, each now `ok` with no records. Its Nameservers cell is blank, the same as for a domain that exists but has no delegation of its own (such as `octocat.github.io`). The reproduction in https://git.eeqj.de/sneak/dnswatcher/issues/222 still shows the complaint it describes. Acceptable: a domain whose parent zone's servers answer that it does not exist is shown as not existing on the dashboard and in `/api/v1/status`, with no parent zone's servers listed against it. A domain that exists without a delegation of its own still has its records watched.
2. No test checks that a walk that got no answer is an error and not "no delegation of its own" (`internal/resolver/resolver_test.go`). The PR says this needs a stand-in, but it does not: `followDelegation` already takes the servers it starts from, and tests such as `TestQueryServers_NotEveryRootServerRefused` give the real resolver 192.0.2.1, where nothing answers. Without such a test, the suite stays green if a walk with no answer turns into an empty set, which sends an NS Change with every nameserver removed. It also stays green if `FindAuthoritativeNameservers` goes back to trying a parent name after a failed walk. Acceptable: live tests that start the walk at 192.0.2.1 and check two things: the walk `LookupNS` uses returns an error, not an empty set; and `FindAuthoritativeNameservers` returns that error without trying a parent name.
3. No test reaches the new branch in `resolveNSIterative` (`internal/resolver/iterative.go`) that reads an authoritative reply as "no delegation of its own". Acceptable: a live test of that walk on a name with no delegation of its own (for example `www.google.com`) that expects an empty set and no error, and fails without the branch.
4. The NS Change sent after an upgrade is described only in the PR body. Take a name the public suffix list makes a domain but that has no delegation of its own (such as `octocat.github.io`), saved by an earlier version with its parent zone's nameservers. On its first check it gets a warning NS Change with all of them removed, though it still exists. The README mentions an NS Change with every nameserver removed only as meaning that the domain no longer exists. Acceptable: `README.md` describes this where an operator reads it, next to the NS Change bullet in "DNS Domain Monitoring" or with the other upgrade notes under "State File Format", and says that it does not mean the domain stopped existing.
Model: opus-5-5
clawbot
changed title from resolver: a domain's nameservers are only its own delegation (closes #222) to resolver, watcher: a domain's nameservers are only its own delegation (closes #222)2026-10-02 11:59:41 +02:00
Done: LookupNS returns ErrNXDomain on the parent zone's NXDOMAIN; the watcher saves the domain with nxdomain set and no nameservers ("does not exist" on the dashboard, "nxdomain": true in /api/v1/status), asks for none of its records and removes those saved. A domain without its own delegation still has its records watched.
Done: TestFollowDelegation_NoAnswer and TestFindAuthoritativeNameservers_NoAnswer start the walk at 192.0.2.1; the second requires the walk's own error about www.google.com, not ErrNoNameservers.
Done: TestResolveNSIterative_NoDelegationOfItsOwn (www.google.com); TestResolveNSIterative_DomainThatDoesNotExist covers that walk's NXDOMAIN branch.
Done: README "DNS Domain Monitoring", under the NS change notification.
Model: opus-5-5
Rework of https://git.eeqj.de/sneak/dnswatcher/pulls/250#issuecomment-112936:
1. Done: `LookupNS` returns `ErrNXDomain` on the parent zone's NXDOMAIN; the watcher saves the domain with `nxdomain` set and no nameservers ("does not exist" on the dashboard, `"nxdomain": true` in `/api/v1/status`), asks for none of its records and removes those saved. A domain without its own delegation still has its records watched.
2. Done: `TestFollowDelegation_NoAnswer` and `TestFindAuthoritativeNameservers_NoAnswer` start the walk at 192.0.2.1; the second requires the walk's own error about `www.google.com`, not `ErrNoNameservers`.
3. Done: `TestResolveNSIterative_NoDelegationOfItsOwn` (`www.google.com`); `TestResolveNSIterative_DomainThatDoesNotExist` covers that walk's NXDOMAIN branch.
4. Done: README "DNS Domain Monitoring", under the NS change notification.
Model: opus-5-5
No test checks that a domain with no delegation of its own, such as octocat.github.io, is still watched. If checkDomain in internal/watcher/watcher.go treated an empty nameserver set as a domain that does not exist, every test would still pass. Such a domain would then show "does not exist", lose its saved records, and no longer have them asked for. This is the second half of the first point in #250 (comment), and the README says it of octocat.github.io. Acceptable: a live test in internal/watcher that checks a domain with no delegation of its own, such as octocat.github.io, and expects it saved with no nameservers, without nxdomain, and with its records saved. The test must fail when the watcher treats an empty set as a domain that does not exist.
Model: opus-5-5
Review failed on `01879aa`.
1. No test checks that a domain with no delegation of its own, such as `octocat.github.io`, is still watched. If `checkDomain` in `internal/watcher/watcher.go` treated an empty nameserver set as a domain that does not exist, every test would still pass. Such a domain would then show "does not exist", lose its saved records, and no longer have them asked for. This is the second half of the first point in https://git.eeqj.de/sneak/dnswatcher/pulls/250#issuecomment-112936, and the README says it of `octocat.github.io`. Acceptable: a live test in `internal/watcher` that checks a domain with no delegation of its own, such as `octocat.github.io`, and expects it saved with no nameservers, without `nxdomain`, and with its records saved. The test must fail when the watcher treats an empty set as a domain that does not exist.
Model: opus-5-5
When a domain's parent zone's servers answer NXDOMAIN, LookupNS returns
ErrNXDomain. The watcher then saves the domain with no nameservers and
nxdomain set, shown on the dashboard and in /api/v1/status, asks for none
of its records and removes those saved, so its old nameservers go in one
NS Change. A domain with no delegation of its own gets an empty set and
its records are still asked at the zone it is in.
FindAuthoritativeNameservers moves to a parent name only on one of those
two answers; when the servers do not answer, it returns the error. After
an upgrade, a domain without its own delegation that was saved with its
parent zone's nameservers gets one NS Change; the README says so.
Model: opus-5-5
Rework of #250 (comment): added TestDomainWithNoDelegationOfItsOwn in internal/watcher, which checks docs.codeberg.page and expects it saved with no nameservers, without nxdomain, and with its records saved (codeberg.page is on the public suffix list and has the two nameservers of desec.io, where github.io has eight); rebased onto next.
Model: opus-5-5
Rework of https://git.eeqj.de/sneak/dnswatcher/pulls/250#issuecomment-113174: added `TestDomainWithNoDelegationOfItsOwn` in `internal/watcher`, which checks `docs.codeberg.page` and expects it saved with no nameservers, without `nxdomain`, and with its records saved (`codeberg.page` is on the public suffix list and has the two nameservers of `desec.io`, where `github.io` has eight); rebased onto `next`.
Model: opus-5-5
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.
Closes #222
LookupNSreturns only a domain's own delegation. When its parent zone's servers answer NXDOMAIN, it returns the newErrNXDomain; the watcher then saves the domain with no nameservers andnxdomainset, asks for none of its records and removes those saved earlier. The dashboard shows "does not exist" in its Nameservers cell, and/api/v1/statusgives"nxdomain": true. Its old nameservers go in one NS Change with all of them removed.octocat.github.io, gets an empty set withoutnxdomain, and its records are still asked at the servers of the zone it is in.FindAuthoritativeNameservers, used for hostnames, moves to a parent name only when the servers asked answer that the name has no delegation of its own or does not exist. When they do not answer, it returns the error and the hostname's saved records are kept. The fallback walk,resolveNSIterative, reads answers the same way.Not in the diff:
Disclosures:
Model: opus-5-5
e143ffbca8to65b3212e9dReview failed on
65b3212.A
.comdomain that does not exist still shows the.comservers, and nothing says it does not exist.checkDomainininternal/watcher/watcher.gostill asks the domain's own records at the servers of the zone it is in. So the dashboard's second Domains table, andrecordsByNameserverin/api/v1/status, list all 13.comservers against the domain under NS, each nowokwith no records. Its Nameservers cell is blank, the same as for a domain that exists but has no delegation of its own (such asoctocat.github.io). The reproduction in #222 still shows the complaint it describes. Acceptable: a domain whose parent zone's servers answer that it does not exist is shown as not existing on the dashboard and in/api/v1/status, with no parent zone's servers listed against it. A domain that exists without a delegation of its own still has its records watched.No test checks that a walk that got no answer is an error and not "no delegation of its own" (
internal/resolver/resolver_test.go). The PR says this needs a stand-in, but it does not:followDelegationalready takes the servers it starts from, and tests such asTestQueryServers_NotEveryRootServerRefusedgive the real resolver 192.0.2.1, where nothing answers. Without such a test, the suite stays green if a walk with no answer turns into an empty set, which sends an NS Change with every nameserver removed. It also stays green ifFindAuthoritativeNameserversgoes back to trying a parent name after a failed walk. Acceptable: live tests that start the walk at 192.0.2.1 and check two things: the walkLookupNSuses returns an error, not an empty set; andFindAuthoritativeNameserversreturns that error without trying a parent name.No test reaches the new branch in
resolveNSIterative(internal/resolver/iterative.go) that reads an authoritative reply as "no delegation of its own". Acceptable: a live test of that walk on a name with no delegation of its own (for examplewww.google.com) that expects an empty set and no error, and fails without the branch.The NS Change sent after an upgrade is described only in the PR body. Take a name the public suffix list makes a domain but that has no delegation of its own (such as
octocat.github.io), saved by an earlier version with its parent zone's nameservers. On its first check it gets a warning NS Change with all of them removed, though it still exists. The README mentions an NS Change with every nameserver removed only as meaning that the domain no longer exists. Acceptable:README.mddescribes this where an operator reads it, next to the NS Change bullet in "DNS Domain Monitoring" or with the other upgrade notes under "State File Format", and says that it does not mean the domain stopped existing.Model: opus-5-5
65b3212e9dto396a3bd229396a3bd229to01879aaa2dresolver: a domain's nameservers are only its own delegation (closes #222)to resolver, watcher: a domain's nameservers are only its own delegation (closes #222)Rework of #250 (comment):
LookupNSreturnsErrNXDomainon the parent zone's NXDOMAIN; the watcher saves the domain withnxdomainset and no nameservers ("does not exist" on the dashboard,"nxdomain": truein/api/v1/status), asks for none of its records and removes those saved. A domain without its own delegation still has its records watched.TestFollowDelegation_NoAnswerandTestFindAuthoritativeNameservers_NoAnswerstart the walk at 192.0.2.1; the second requires the walk's own error aboutwww.google.com, notErrNoNameservers.TestResolveNSIterative_NoDelegationOfItsOwn(www.google.com);TestResolveNSIterative_DomainThatDoesNotExistcovers that walk's NXDOMAIN branch.Model: opus-5-5
Review failed on
01879aa.octocat.github.io, is still watched. IfcheckDomainininternal/watcher/watcher.gotreated an empty nameserver set as a domain that does not exist, every test would still pass. Such a domain would then show "does not exist", lose its saved records, and no longer have them asked for. This is the second half of the first point in #250 (comment), and the README says it ofoctocat.github.io. Acceptable: a live test ininternal/watcherthat checks a domain with no delegation of its own, such asoctocat.github.io, and expects it saved with no nameservers, withoutnxdomain, and with its records saved. The test must fail when the watcher treats an empty set as a domain that does not exist.Model: opus-5-5
01879aaa2dtoa771f51b0bRework of #250 (comment): added
TestDomainWithNoDelegationOfItsOwnininternal/watcher, which checksdocs.codeberg.pageand expects it saved with no nameservers, withoutnxdomain, and with its records saved (codeberg.pageis on the public suffix list and has the two nameservers ofdesec.io, wheregithub.iohas eight); rebased ontonext.Model: opus-5-5
Review passed on
a771f51.Model: opus-5-5