isitdns? wiki/Tools/Ask DNS if it is DNS
Tools

Ask DNS if it is DNS

Asks the site itself, over DNS, what it can answer.

 dig -4 @1.1.1.1 help.isitdns.net TXT
; <<>> DiG 9.20.11-0ubuntu0.2-Ubuntu <<>> -4 @1.1.1.1 help.isitdns.net TXT
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 14887
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;help.isitdns.net. IN TXT
;; ANSWER SECTION:
help.isitdns.net. 60 IN TXT "is it DNS? no. isitdns monitors common public DNS providers. as of 2026-10-06T21:22:20Z" "ask: isit why ksk cloudflare google quad9 adguard, all .isitdns.net TXT; A ladder 127.0.0.2 no, .3 mostly, .4 maybe, .5 no data; 127.0.0.1 is never ours; tokens, one string in isit and here: state (ok, degraded, failing, stale), bad, measured, probes, " "asof; concatenate the strings of a line, no separator" "receipt: https://isitdns.net; probe: ours not yours, your path: https://isitdns.net/wiki/troubleshooting/test-your-own-path" "state=ok bad=0 measured=17 probes=4 seats=4 asof=2026-10-06T21:22:20Z"
;; Query time: 36 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Tue Oct 06 16:24:21 CDT 2026
;; MSG SIZE rcvd: 646

GO NOERROR and the help text in TXT strings

NO GO NXDOMAIN, which means you are not reaching the isitdns.net zone

what each part of this output means →

Captured 2026-10-06 at 21:24:21Z, at surface=8: 1.1.1.1 over IPv4, no port-53 interception on the path.

The names#

NameWhat comes back
help.isitdns.netTXT: the verdict line, the menu, the receipt, the token line. A: the word as a ladder code
isit.isitdns.netTXT: the token line, then the word: no, mostly, maybe or no data. A: the word as a ladder code
why.isitdns.netTXT: the resolvers that are not ok, or all measured resolvers answering
ksk.isitdns.netTXT: the root KSKs published, the roll date, and the public resolvers by sentinel result
cloudflare.isitdns.net, google.isitdns.net, quad9.isitdns.net, adguard.isitdns.netTXT: that resolver's status line, then one string per measured leg
_isitdns.isitdns.netTXT: the version record

Every record has a TTL of 60 seconds, except the version record, which has 3600.

The version record#

dig +short _isitdns.isitdns.net TXT
"v=isit1 surface=8 round=180 spec=https://isitdns.net/wiki/tools/ask-dns"

v=isit1 is the format of this record. surface=8 is the version of the records on this page. round is how often the zone changes, in seconds. spec is this page.

The tokens, for a script#

isit and help each carry one token line of key=value pairs.

TokenValue
stateok for no, degraded for mostly, failing for maybe, stale for no data
badresolvers failing from every network that read them; state=degraded with bad=0 means one or more failed from some networks and answered from others
measuredresolvers with a verdict
probesthe number of probes the verdict was read from this round; 0 when there is no reading
seatsthe same value as probes, kept for parsers of the old key, removed at surface=9
asofthe stamp of the verdict line, RFC 3339 with Z

The menu in help lists probes, not seats (legacy key).

The A record ladder#

help and isit answer an A query with a loopback code, the DNSxL convention of RFC 5782 section 2.1.

AddressWord
127.0.0.2no
127.0.0.3mostly
127.0.0.4maybe
127.0.0.5no data

127.0.0.1 is never a code (RFC 5782 section 5). A 127.0.0.1 answer for these names did not come from isitdns. Any other address is not a word.

dig +short isit.isitdns.net A

The grammar#

ABNF, RFC 5234. Quoted strings are case-sensitive, the %s form of RFC 7405.

  • A TXT record is one or more character-strings of at most 255 octets (RFC 1035 section 3.3.14). Concatenate the strings of a record with nothing between them (RFC 7208 section 3.3).
  • help is four parts, help-1 to help-4, each found by its prefix: is it DNS? , ask: , receipt: , state=. A part spans one or more strings.
  • isit is exactly two strings: isit-1, then isit-2.
  • A resolver record matches resolver once its strings are concatenated. Each leg is one string beginning v4 or v6 ; every string but the last ends in SP.
  • These records end in as of <time>Z: why, ksk. A resolver record ends in a leg string when it has one.
  • The version record is one string matching version-record (rule 1). The token line matches token-line (rule 8).
stamp          = 4DIGIT "-" 2DIGIT "-" 2DIGIT "T" 2DIGIT ":" 2DIGIT ":" 2DIGIT "Z"
day            = 4DIGIT "-" 2DIGIT "-" 2DIGIT
as-of          = "as of " stamp
count          = "0" / %x31-39 *DIGIT
word           = "no" / "mostly" / "maybe" / "no data"
help-1         = "is it DNS? " verdict " isitdns monitors common public DNS providers. " [ count " no data, " ] as-of
verdict        = "no."
                 / "mostly. " count " of " count " resolvers are struggling."
                 / "maybe. " count " of " count " resolvers are down."
                 / "no data. " nd-reason "."
                 / "no data yet. " yet-reason "."
nd-reason      = "fewer than two networks have reported; the last reading is " age " old"
                 / "the probe readings could not be read"
                 / "the board round's reading is stamped in the future; no verdict until the clocks agree"
                 / "no database is bound; nothing was read"
yet-reason     = "no probe has reported"
reason         = nd-reason / yet-reason
age            = count "m" / count "h " count "m" / count "d"
help-2         = "ask: isit why ksk cloudflare google quad9 adguard, all .isitdns.net TXT; A ladder 127.0.0.2 no, .3 mostly, .4 maybe, .5 no data; 127.0.0.1 is never ours; tokens, one string in isit and here: state (ok, degraded, failing, stale), bad, measured, probes, asof; concatenate the strings of a line, no separator"
help-3         = "receipt: https://isitdns.net; probe: ours not yours, your path: https://isitdns.net/wiki/troubleshooting/test-your-own-path"
help-4         = tokens
tokens         = "state=" state " bad=" count " measured=" count " probes=" count " seats=" count " asof=" stamp
state          = "ok" / "degraded" / "failing" / "stale"
version        = "v=isit1 surface=" count " round=" count " spec=" spec-url
spec-url       = "https://isitdns.net/" 1*( %x61-7A / DIGIT / "-" / "." / "_" / "~" / "/" )
version-record = "v=isit1" *( SP vtag )
vtag           = vkey "=" vval
vkey           = 1*( %x61-7A / DIGIT )
vval           = 1*%x21-7E
token-line     = token *( SP token )
token          = tkey "=" tval
tkey           = 1*%x61-7A
tval           = 1*%x21-7E
isit           = isit-1 isit-2
isit-1         = tokens
isit-2         = word
ladder-code    = "127.0.0.2" / "127.0.0.3" / "127.0.0.4" / "127.0.0.5"
why            = not-fine ", " as-of
                 / "all measured resolvers answering, " as-of
                 / "no data: " reason ", " as-of
not-fine       = rstate *( ", " rstate )
rstate         = board-id " " ( "slow" / "partial" / "bad" )
board-id       = ldh-label
ldh-label      = alnum [ *38( alnum / "-" ) alnum ]
alnum          = %x61-7A / DIGIT
resolver       = status *( SP leg )
resolver-id    = "cloudflare" / "google" / "quad9" / "adguard"
status         = resolver-id " " rstatus " " latency [ ", " note ] ", " dnssec ", " as-of
                 / resolver-id " no data, " as-of
rstatus        = "ok" / "slow" / "partial" / "bad"
note           = "failing from " dlist "; slow from " dlist / "failing from " dlist / "slow from " dlist
dlist          = dname *( ", " dname )
latency        = count " ms" [ " from " dname ] / "unreachable" / "n/a"
dname          = probe-name / domain-id
probe-name     = "the" 1*%x61-7A
domain-id      = 1*%x61-7A
dnssec         = "dnssec validating" / "dnssec not validating" / "dnssec unknown"
leg            = family " " transport " " leg-latency " " leg-state
family         = "v4" / "v6"
transport      = "doh" / "dot" / "tcp53" / "udp53"
leg-latency    = count "ms" / "--"
leg-state      = "ok" [ SP rcode ]
                 / ( "unreachable" / "failing" ) [ " from " probe-list ]
probe-list     = probe-name [ *( ", " probe-name ) " and " probe-name ]
rcode          = 1*%x41-5A
ksk            = keys "; roll " day "; of " count " public resolvers via sentinel: " count " trust " tag ", "
                 count " do not yet, " count " no sentinel support, " count " not validating, "
                 count " no determination (both SERVFAIL), " count " unreadable; " as-of
keys           = "root KSKs published: " tag *( ", " tag ) / "root KSKs: unread"
tag            = 1*5DIGIT

Reading it as a client#

  1. The version record is one TXT record at _isitdns.isitdns.net that matches version-record.
  2. A record whose first tag is not v=isit1 is not an isitdns version record, and a client MUST stop reading it.
  3. A client MUST ignore a version tag it does not know.
  4. More than one version record, no version record, a version key twice, or a surface that is absent or not a count of at most 9 digits means the version is unknown; round and spec do not change it.
  5. A client that reads a surface it does not implement, or an unknown version, MUST NOT make a verdict from these records and MUST NOT report a DNS outage.
  6. Such a client MAY show the raw records and the surface number.
  7. surface versions these DNS records only; the MCP server is versioned by its own serverInfo.version and its tools/list.
  8. The token line matches token-line: keys separated by single spaces, each key once, order not significant.
  9. A client MUST ignore a token key it does not know.
  10. state and asof are required; a token line without both, or with a key twice, is not usable.
  11. An absent key is not stated; it never means 0.
  12. A stamp 600 s old or older, or more than 180 s ahead, is not live: live is -180 <= age < 600, where age is the client's UTC clock minus the stamp, in seconds. A stamp that is not a valid RFC 3339 date-time has no age and is not live; a client that knows its clock is not set MUST NOT make a verdict.
  13. A stamp that is not live is no data, whatever the word beside it says.
  14. The freshness rule covers help, isit, why and the resolver records, and ksk with 1200 s in place of 600 s.
  15. When the TXT stamp is live, the TXT record wins over the A record.
  16. An A answer without a live TXT stamp beside it is no data.
  17. isitdns.net is DNSSEC-signed. A client SHOULD validate the answer itself, or accept AD=1 only from a validating resolver it trusts over a secure channel (RFC 4035 section 4.9.3); it SHOULD label any other answer unauthenticated, a label that does not change the verdict.
  18. A bogus answer (SERVFAIL, or extended DNS error 6 or 9 of RFC 8914) MUST NOT be read as a verdict.
  19. NOERROR with a record is the answer.
  20. NOERROR with no record, or NXDOMAIN, for a name not on this page means it is not on this surface. For a name on this page, NOERROR with no record is no reading, and NXDOMAIN was made on the path, except ksk, absent while its board is refused: NXDOMAIN or NOERROR with no record for ksk is no reading. None of these is a verdict.
  21. SERVFAIL, REFUSED, a timeout, or TC=1 without a retry over TCP is no reading, never a verdict.
  22. Each name carries one TXT record, and help and isit one A record; more than one is no reading for that name.
  23. A surface change passes through an unknown version: the version record drops surface at least 3600 s before the records change, and the new surface appears at least 60 s after them.
  24. A resolver record carries each family and transport pair at most once, order not significant; with a pair twice, its legs are not usable.
  25. A help or isit TXT whose word (no data yet is no data) and state disagree, or whose as of and asof differ, is no reading.

This reader applies rules 10, 12, 13 and 25 to isit:

dig +short isit.isitdns.net TXT | python3 -c '
import calendar, math, re, sys, time
a = sys.stdin.read()
p = re.findall(r"(\w+)=([^\s\"]+)", a)
t = dict(p)
for k in ("state", "asof"):
    if k not in t: sys.exit("not fine: no %s token in the answer" % k)
if len(t) != len(p): sys.exit("not fine: a token key appears twice")
w = {"no": "ok", "mostly": "degraded", "maybe": "failing", "no data": "stale"}
if w.get((re.findall(r"\"([^\"]*)\"", a) or [""])[-1]) != t["state"]: sys.exit("not fine: word and state disagree")
if not re.fullmatch(r"\d{4}-\d\d-\d\d[Tt]([01]\d|2[0-3]):[0-5]\d:([0-5]\d|60)[Zz]", t["asof"]): sys.exit("not fine: asof is not a valid stamp")
try: age = math.floor(time.time() - calendar.timegm(time.strptime(t["asof"].upper(), "%Y-%m-%dT%H:%M:%SZ")))
except ValueError: sys.exit("not fine: asof is not a valid stamp")
ok = t["state"] == "ok" and -180 <= age < 600
print("fine, %ds old" % age if ok else "not fine: state=%s, %ds old" % (t["state"], age))'

See also#

  • dig-tool runs the same queries from the browser
  • dig-flags explains +short and the rest
  • when-its-dns is what to do when the word is not no
  • txt is the record type doing the work here