DoH Wire Format (RFC 8484)
Send application-scoped ResolveDB DNS queries over HTTPS using RFC 8484 wire format.
Endpoint
https://doh.resolvedb.io/dns-queryResolveDB is authoritative-only. Use this endpoint for ResolveDB qnames; do not configure it as a browser or operating-system resolver for unrelated domains.
GET
Build a DNS message, encode it as unpadded Base64url, and send it in the dns
query parameter:
GET /dns-query?dns={base64url-dns-message}
Accept: application/dns-messageThe encoded parameter is limited to 8 KiB and the decoded DNS message is limited to 4 KiB. Generate the bytes with a DNS library rather than hand-encoding names.
POST
Send a DNS wire message as the request body:
POST /dns-query
Content-Type: application/dns-message
Accept: application/dns-message
{binary DNS message}The request body is limited to 4 KiB.
Python Example
import dns.message
import dns.query
query = dns.message.make_query(
"get.newyork.weather.public.v1.resolvedb.net",
"TXT",
)
response = dns.query.https(
query,
"https://doh.resolvedb.io/dns-query",
)
for rrset in response.answer:
for record in rrset:
print(record.to_text())Go Example
package main
import (
"encoding/base64"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"github.com/miekg/dns"
)
func main() {
message := new(dns.Msg)
message.SetQuestion("get.newyork.weather.public.v1.resolvedb.net.", dns.TypeTXT)
wire, err := message.Pack()
if err != nil {
panic(err)
}
encoded := base64.RawURLEncoding.EncodeToString(wire)
endpoint := "https://doh.resolvedb.io/dns-query?dns=" + url.QueryEscape(encoded)
request, err := http.NewRequest(http.MethodGet, endpoint, nil)
if err != nil {
panic(err)
}
request.Header.Set("Accept", "application/dns-message")
response, err := http.DefaultClient.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
answer := new(dns.Msg)
if err := answer.Unpack(body); err != nil {
panic(err)
}
for _, rr := range answer.Answer {
if txt, ok := rr.(*dns.TXT); ok {
fmt.Println(strings.Join(txt.Txt, ""))
}
}
}Responses
Successful HTTP handling returns application/dns-message; inspect the DNS
RCODE for query-level failures.
| RCODE | Meaning |
|---|---|
NOERROR | Answer or NODATA |
FORMERR | Malformed UQRP input |
SERVFAIL | Service or storage failure |
REFUSED | Authorization, namespace, or reserved-resource denial |
Wire responses with answers use Cache-Control: max-age=<minimum DNS TTL>.
Errors, empty answers, and private TTL-0 answers use Cache-Control: no-store.
TXT records may contain multiple 255-byte character strings; concatenate them in
order before parsing the UQRP envelope.
HTTP Errors
| Status | Meaning |
|---|---|
200 | DNS response returned; inspect its RCODE |
400 | Missing, malformed, or oversized DNS message |
406 | Accept does not allow application/dns-message |
415 | POST body has the wrong media type |
Shared Answering Contract (Implemented)
The shared answering contract requires wire DoH and native DNS to preserve the same product value, RCODE, and effective DNS TTL. Requested DNSSEC material uses the same live zone/key lifecycle, with supporting DNSKEY access; required signing failure becomes a DNS failure. Query statistics include DoH.
Shared schema/zone and units/moon/gated-BTC answering are implemented in the
repository, including those value/RCODE/TTL/signing guarantees. Computed ordinary
answers retain flat fields after v=rdb1;s=ok;t=data; BTC remains mock-only and
gated off. Hosted private/public-read/demo records now share these guarantees:
private hits and negatives carry TTL 0 through SOA/proofs, public positive TTLs
are capped at record expiry, and corrupt values return SERVFAIL. All-family
evaluation statistics include DoH. Weather, forecast,
sun, and GeoIP share output/signing/cache policy too: forecast retains daily
provider values and TTL at most 1800; daily results are capped at UTC midnight.
Dataset manifest/keys/identity answering and analytics are also shared. Statistics
identify the evaluated family: hosted units is records, datasets are
datasets. Only verified private authorization supplies tenant attribution,
including on misses and expiry. Public/schema/dataset reads remain unattributed.
Complete repository conformance does not imply production rollout or package publication.
Product answers are TXT-only; ordinary zone and DNSSEC support records are
exempt. Cacheable TTLs are bounded by known expiry or validity transitions, and
authenticated positive and negative answers are uncacheable. Encoding the entire
DNS message in the GET dns= parameter remains unpadded Base64url; retiring
Base64 inside query labels does not change RFC 8484 framing.