Prometheus exporter for DNSTT client/session metrics.
Find a file
2026-05-27 09:09:35 +02:00
cmd/dnstt_exporter add geoip country/asn labels and ipv6 2026-05-05 13:57:12 +02:00
internal/dnstt Reduce peak client recalculation overhead 2026-05-27 09:09:35 +02:00
.envrc init devshell 2026-05-05 13:28:32 +02:00
.gitignore initial working version 2026-05-05 13:43:02 +02:00
flake.lock init devshell 2026-05-05 13:28:32 +02:00
flake.nix Add nixos-module 2026-05-05 14:07:27 +02:00
go.mod add geoip country/asn labels and ipv6 2026-05-05 13:57:12 +02:00
go.sum add geoip country/asn labels and ipv6 2026-05-05 13:57:12 +02:00
nixos-module.nix Add nixos-module 2026-05-05 14:07:27 +02:00
README.md Document Grafana queries 2026-05-27 09:09:35 +02:00
renovate.json Add renovate.json 2026-05-20 09:40:52 +00:00

dnstt_exporter

Prometheus exporter for DNSTT client/session metrics.

dnstt_exporter observes DNSTT DNS traffic on a local Linux host and exports aggregate Prometheus metrics. It does not proxy, terminate, or configure DNSTT; it passively decodes DNSTT session IDs from DNS query names.

Usage

sudo dnstt_exporter \
  -dnstt.domain tunnel.example.com \
  -dnstt.port 53 \
  -geoip.country-database /path/to/GeoLite2-Country.mmdb \
  -geoip.asn-database /path/to/GeoLite2-ASN.mmdb \
  -web.listen-address :9713

The exporter needs permission to open an AF_PACKET raw socket. Run it as root or grant the binary CAP_NET_RAW.

Metrics are served at http://127.0.0.1:9713/metrics by default.

How It Works

dnstt_exporter opens a Linux AF_PACKET raw socket and passively watches UDP DNS traffic on the configured DNSTT port. It parses IPv4 and IPv6 packets, matches DNS query names against the configured DNSTT domain, and decodes the DNSTT session ID from the query-name prefix.

The exporter treats a session as active when it has seen a query for that session within the last 30 seconds. Peak client counts are the highest active session counts observed since the exporter started.

GeoIP labels are based on the resolver address seen by the server. For incoming queries this is the packet source address; for outgoing responses it is the packet destination address. This may be a recursive resolver such as an ISP DNS server, Cloudflare, Google, or Quad9, not the original DNSTT client.

The exporter does not run dnstt-server, proxy traffic, terminate DNSTT, or decrypt tunnel payloads.

Metrics

All DNSTT metrics use a domain label. If -geoip.country-database is set, metrics also include country. If -geoip.asn-database is set, metrics also include asn. Unmapped countries use ZZ; unmapped ASNs use 0.

  • dnstt_active_clients
  • dnstt_peak_clients
  • dnstt_queries_total
  • dnstt_bytes_in_total
  • dnstt_bytes_out_total
  • dnstt_sessions_total

Grafana Queries

Use $domain as a Grafana variable for the DNSTT domain, for example t2.bypasscensorship.org. Replace $__rate_interval with a fixed range such as 5m if you are not using Grafana's built-in interval variables.

  • Title: Current active clients
  • Description: Current number of active DNSTT sessions for the selected domain.
  • Visualization: Stat or gauge.
sum(dnstt_active_clients{domain="$domain"})
  • Title: Peak active clients
  • Description: Highest active DNSTT session count observed since the exporter started.
  • Visualization: Stat.
sum(dnstt_peak_clients{domain="$domain"})
  • Title: Active clients by country
  • Description: Current active DNSTT sessions grouped by resolver country.
  • Visualization: Geomap, bar chart, or table.
sum by (country) (dnstt_active_clients{domain="$domain"})
  • Title: Top countries by active clients
  • Description: Countries with the most active DNSTT sessions right now.
  • Visualization: Bar chart.
topk(10, sum by (country) (dnstt_active_clients{domain="$domain"}))
  • Title: Active clients by ASN
  • Description: Current active DNSTT sessions grouped by resolver ASN.
  • Visualization: Bar chart or table.
sum by (asn) (dnstt_active_clients{domain="$domain"})
  • Title: Top ASNs by active clients
  • Description: Resolver ASNs with the most active DNSTT sessions right now.
  • Visualization: Bar chart.
topk(10, sum by (asn) (dnstt_active_clients{domain="$domain"}))
  • Title: Active clients by country and ASN
  • Description: Current active DNSTT sessions split by both resolver country and resolver ASN.
  • Visualization: Table.
sum by (country, asn) (dnstt_active_clients{domain="$domain"})
  • Title: Top country/ASN pairs by active clients
  • Description: Country and ASN combinations with the most active DNSTT sessions right now.
  • Visualization: Bar chart or table.
topk(20, sum by (country, asn) (dnstt_active_clients{domain="$domain"}))
  • Title: DNS query rate
  • Description: Total observed DNSTT DNS queries per second for the selected domain.
  • Visualization: Time series.
sum(rate(dnstt_queries_total{domain="$domain"}[$__rate_interval]))
  • Title: DNS query rate by country
  • Description: Observed DNSTT DNS queries per second grouped by resolver country.
  • Visualization: Stacked time series or bar chart. Use stacked time series for trends over time, and bar chart for current top countries.
sum by (country) (rate(dnstt_queries_total{domain="$domain"}[$__rate_interval]))
  • Title: Top ASNs by DNS query rate
  • Description: Resolver ASNs producing the highest DNSTT DNS query rates.
  • Visualization: Bar chart.
topk(10, sum by (asn) (rate(dnstt_queries_total{domain="$domain"}[$__rate_interval])))
  • Title: Inbound DNS traffic rate
  • Description: Bytes per second received in DNSTT DNS queries.
  • Visualization: Time series.
sum(rate(dnstt_bytes_in_total{domain="$domain"}[$__rate_interval]))
  • Title: Outbound DNS traffic rate
  • Description: Bytes per second sent in DNSTT DNS responses.
  • Visualization: Time series.
sum(rate(dnstt_bytes_out_total{domain="$domain"}[$__rate_interval]))
  • Title: DNS traffic rate by country
  • Description: Combined inbound and outbound DNSTT DNS bytes per second grouped by resolver country.
  • Visualization: Geomap, stacked time series, or bar chart.
sum by (country) (
  rate(dnstt_bytes_in_total{domain="$domain"}[$__rate_interval])
  +
  rate(dnstt_bytes_out_total{domain="$domain"}[$__rate_interval])
)
  • Title: Total observed sessions
  • Description: Total unique DNSTT sessions observed since the exporter started.
  • Visualization: Stat.
sum(dnstt_sessions_total{domain="$domain"})
  • Title: New session rate
  • Description: New DNSTT sessions observed per second.
  • Visualization: Time series.
sum(rate(dnstt_sessions_total{domain="$domain"}[$__rate_interval]))
  • Title: New session rate by country
  • Description: New DNSTT sessions per second grouped by resolver country.
  • Visualization: Stacked time series, geomap, or bar chart.
sum by (country) (rate(dnstt_sessions_total{domain="$domain"}[$__rate_interval]))

Development

go test ./...
go build ./cmd/dnstt_exporter