Files
vps/.agents/skills/encrypted-dns-skill

Encrypted DNS Skill

CI

An Agent Skill and deterministic Go CLI for querying and diagnosing encrypted DNS resolvers.

The skill tells an agent when and how to perform encrypted DNS diagnostics; ednsdiag performs the protocol exchange. Agents do not need to construct DoH URLs, TLS sessions, or DNS wire messages themselves.

Status

Protocol Status Standard
DNS over HTTPS (DoH) Available (GET and POST) RFC 8484
DNS over TLS (DoT) Available (strict authentication) RFC 7858, RFC 8310
DNS over QUIC (DoQ) Available RFC 9250
DoH over HTTP/3 (DoH3) Available (GET and POST) RFC 8484 over HTTP/3
DNSCrypt Available (v2 over UDP) DNSCrypt protocol specification
Oblivious DoH (ODoH) Research RFC 9230
Anonymized DNSCrypt Research Anonymized DNSCrypt specification

Run ednsdiag capabilities instead of assuming a protocol is implemented.

Why a Skill and a CLI?

  • SKILL.md provides compact instructions, safety boundaries, and result interpretation for an AI agent.
  • ednsdiag provides repeatable wire-format DNS, HTTP, TLS, input validation, and structured JSON output.
  • Reference files keep protocol, provider, and security details grounded in authoritative sources without bloating the agent's active context.

The CLI never silently downgrades to plaintext DNS, and it does not connect to addresses returned in DNS answers.

Requirements

  • Go 1.26.6 or later when running or building from source
  • Network access to the selected encrypted DNS resolver
  • A host that supports the Agent Skills package format when using the repository as a Skill

Install the Skill

Clone or copy this repository into a skill discovery directory supported by your agent host. Keep the repository layout intact so SKILL.md, references/, schemas/, and the Go source remain together.

For example, in a host that discovers project-local skills from .agents/skills:

git clone https://github.com/windyboy/encrypted-dns-skill.git \
  .agents/skills/encrypted-dns-skill

Discovery paths differ between hosts. Follow the host's documentation rather than moving only SKILL.md.

Run from Source

No precompiled executable is required. Go can compile and run the command from the repository root:

go run ./cmd/ednsdiag capabilities
go run ./cmd/ednsdiag query example.com A --protocol doh --provider cloudflare
go run ./cmd/ednsdiag query gmail.com MX --protocol dot --provider google --timeout 5s
HTTPS_PROXY=http://127.0.0.1:8080 go run ./cmd/ednsdiag query example.com A --protocol doh
go run ./cmd/ednsdiag query example.com A --protocol dot --proxy http://127.0.0.1:8080
go run ./cmd/ednsdiag query example.com AAAA --protocol doq --provider adguard
go run ./cmd/ednsdiag query example.com HTTPS --protocol doh3 --provider cloudflare
go run ./cmd/ednsdiag query example.com A --protocol dnscrypt --provider adguard
go run ./cmd/ednsdiag probe example.com A --protocol dot --provider cloudflare
go run ./cmd/ednsdiag compare example.com A \
  --target doh:cloudflare --target dot:google

The first run may download the modules pinned in go.mod and go.sum.

To build a reusable local executable:

go build -o ./bin/ednsdiag ./cmd/ednsdiag
./bin/ednsdiag capabilities

Do not download or execute an unverified third-party binary. This repository does not currently publish release binaries.

Usage

ednsdiag capabilities
ednsdiag version
ednsdiag query <domain> [type] \
  [--protocol doh|dot|doq|doh3|dnscrypt|odoh|anonymized-dnscrypt] \
  [--provider cloudflare|google|quad9|adguard] \
  [--method post|get] \
  [--proxy http://host:port] \
  [--timeout 5s]
ednsdiag probe <domain> [type] [query options]
ednsdiag compare <domain> [type] \
  --target protocol:provider[:method] \
  --target protocol:provider[:method] \
  [--proxy http://host:port] \
  [--attempt-timeout 5s] [--timeout 30s] [--max-attempts 4]

Defaults are A, doh, cloudflare, post, and 5s. --method applies only to DoH and DoH3. The timeout must be between 250ms and 30s. Research protocols are accepted as inputs so automation receives a structured unsupported result and exit code 4; they are never silently substituted.

DoH and DoT honor Go's standard HTTPS_PROXY/https_proxy and NO_PROXY/no_proxy environment variables. --proxy overrides environment selection and accepts an http:// or https:// proxy URL, including optional Basic-auth userinfo. DoT uses HTTP CONNECT before its resolver TLS handshake. DoH3, DoQ, and DNSCrypt are UDP/QUIC transports and cannot use this TCP CONNECT proxy; an explicit proxy combined with one of those protocols is rejected. The same --proxy is shared by every DoH/DoT target in a compare operation. See Go's official ProxyFromEnvironment documentation for environment-variable and NO_PROXY matching rules.

compare accepts 28 unique, allowlisted targets, bounded by --max-attempts. Its total timeout is 250ms60s; each attempt timeout is 250ms30s and cannot exceed the total. Comparison targets start concurrently, results retain the requested target order, and answers are never merged.

Supported record types are A, AAAA, CNAME, MX, TXT, NS, SOA, CAA, SRV, PTR, HTTPS, and SVCB. For PTR, pass an IP address; the CLI constructs the reverse name. Other IP literals and local names remain blocked.

Built-in resolver profiles:

Provider Profile DoH DoT DoQ DoH3 DNSCrypt
Cloudflare Unfiltered Yes Yes No Yes No
Google Unfiltered Yes Yes No Yes No
Quad9 Security-filtered Yes Yes No No No
AdGuard Ad- and security-filtered Yes Yes Yes No Yes

Filtering policies can affect DNS answers. Results always identify the provider and profile used.

Result Semantics

Every query returns structured JSON compatible with schemas/result-v1.schema.json.

  • completed: true means the encrypted protocol exchange completed; it does not mean the DNS response was NOERROR.
  • dns.rcode is the DNS result. NXDOMAIN, SERVFAIL, and REFUSED are DNS outcomes, not transport failures.
  • Empty dns.answers with NOERROR means NODATA.
  • transport.server_authenticated reports resolver endpoint authentication.
  • dns.resolver_reports_dnssec_authenticated reflects the resolver's AD bit; it is not local DNSSEC validation.
  • transport.bootstrap: system_resolver means the operating system resolver was used to locate the encrypted resolver endpoint.
  • transport.proxy, when present, is the HTTP(S) proxy endpoint actually selected for DoH or DoT. Embedded credentials are never reported.
  • DNSCrypt reports bootstrap: stamp_ip, the authenticated provider name, resolver certificate serial, and selected crypto construction.
  • DoH and DoH3 subtract a valid HTTP Age value from returned answer TTLs and report it as transport.http_age_seconds.
  • Truncated or non-representable DNS answers are protocol failures rather than partial completed: true results.

Human-readable usage errors go to stderr. Machine-readable operational results go to stdout. Stable exit codes are:

Code Meaning
0 The requested encrypted DNS operation completed; inspect dns.rcode.
1 Local or internal failure.
2 Invalid input or CLI usage.
3 Transport or DNS protocol failure.
4 Known but unsupported capability or provider/protocol combination.

See references/contracts.md for the complete v1 command and result contract.

Security Model

  • DoH uses standard application/dns-message wire messages.
  • DoT verifies the PKIX certificate chain and configured authentication domain.
  • DoT advertises ALPN dot; an empty selection is accepted and reported, while selection of a different application protocol is rejected.
  • DNSCrypt validates the resolver stamp, Ed25519-signed certificate, validity interval, provider identity, and encrypted response before accepting DNS data.
  • Plaintext fallback is prohibited.
  • DNS errors are not retried through another protocol as transport failures.
  • Provider and protocol results remain separate.
  • DNS answers are data only; the tool does not make application connections to returned addresses.

See references/security.md for the complete threat model and privacy boundaries.

Development

go test ./...
go test -race ./...
go vet ./...
go run honnef.co/go/tools/cmd/staticcheck@v0.7.0 ./...
go run golang.org/x/vuln/cmd/govulncheck@v1.6.0 ./...
python3 scripts/validate_skill.py .

Public endpoint interoperability tests are opt-in and skip cleanly when the host cannot reach the network:

EDNSDIAG_PUBLIC_INTEROP=1 go test ./internal/edns \
  -run '^TestPublicCloudflareDo[HT]Interoperability$' -count=1 -v

Protocol behavior must remain aligned with references/standards.md, provider changes with references/providers.md, and output with the v1 JSON schema.

Scope

This project targets client-to-recursive encrypted DNS diagnostics. It is not a system stub resolver, an authoritative DNS server, a hosted DNS record manager, or a zone-transfer tool.

License

Licensed under the Apache License 2.0.