[server]
bind_addr = "0.0.0.0:53"
api_port = 5380
# api_bind_addr = "127.0.0.1"  # default; set to "0.0.0.0" for LAN dashboard access
# data_dir = "/var/lib/numa"  # where numa stores TLS CA and cert material
                              # Defaults: /var/lib/numa on linux (FHS),
                              # /usr/local/var/numa on macos (homebrew prefix),
                              # %PROGRAMDATA%\numa on windows. Override for
                              # containerized deploys or tests that can't
                              # write to the system path.
# filter_aaaa = true          # on IPv4-only networks, answer AAAA queries with
                              # NODATA (NOERROR + empty answer) so Happy Eyeballs
                              # clients don't wait on a v6 attempt that can't
                              # succeed. Also strips `ipv6hint` from HTTPS/SVCB
                              # records (RFC 9460) so modern browsers (Chrome
                              # ≥103, Firefox, Safari) don't bypass the AAAA
                              # filter via SVCB hints. Local zones, overrides,
                              # and the .numa proxy are NOT filtered — you can
                              # still configure v6 records for local services.
                              # Default: false.

# [upstream]
# mode = "forward"                              # "forward" (default) — relay to upstream
#                                               # "recursive" — resolve from root hints (no address needed)
#                                               # "odoh" — Oblivious DoH (see ODoH block below)
# address = "9.9.9.9"                           # single upstream (plain UDP)
# address = ["192.168.1.1", "9.9.9.9:5353"]    # multiple upstreams — SRTT picks fastest
# address = "https://dns.quad9.net/dns-query"   # DNS-over-HTTPS (encrypted)
# address = "tls://9.9.9.9#dns.quad9.net"       # DNS-over-TLS (encrypted, port 853)
# fallback = ["8.8.8.8", "1.1.1.1"]            # tried only when all primaries fail
# port = 53                                     # default port for addresses without :port
# timeout_ms = 3000
# hedge_ms = 0                                   # request hedging delay (ms). Default: 0 (off).
#                                                # Set to e.g. 10 to fire a parallel upstream
#                                                # request after 10ms of silence — rescues packet
#                                                # loss (UDP), dispatch spikes (DoH), TLS stalls
#                                                # (DoT). Doubles the upstream query count, so
#                                                # leave off for quota'd providers (NextDNS,
#                                                # Control D).

# ODoH (Oblivious DNS-over-HTTPS, RFC 9230). The relay sees your IP but
# not the question; the target sees the question but not your IP. Numa
# refuses same-operator relay+target configs by default (eTLD+1 check).
# [upstream]
# mode = "odoh"
# relay = "https://odoh-relay.numa.rs/relay"
# target = "https://odoh.cloudflare-dns.com/dns-query"
# strict = true                                 # default: refuse to downgrade to `fallback`
#                                                # on relay failure. Set false to allow a
#                                                # non-oblivious fallback path.
# relay_ip = "178.104.229.30"                   # optional: pin IPs so numa doesn't leak the
# target_ip = "104.16.249.249"                  # relay/target hostnames via the bootstrap
#                                                # resolver on cold boot when numa is its
#                                                # own system DNS. See
#                                                # recipes/odoh-upstream.md.
# root_hints = [                                # only used in recursive mode
#     "198.41.0.4",      # a.root-servers.net (Verisign)
#     "199.9.14.201",    # b.root-servers.net (USC-ISI)
#     "192.33.4.12",     # c.root-servers.net (Cogent)
#     "199.7.91.13",     # d.root-servers.net (UMD)
#     "192.203.230.10",  # e.root-servers.net (NASA)
#     "192.5.5.241",     # f.root-servers.net (ISC)
#     "192.112.36.4",    # g.root-servers.net (US DoD)
#     "198.97.190.53",   # h.root-servers.net (US Army)
#     "192.36.148.17",   # i.root-servers.net (Netnod)
#     "192.58.128.30",   # j.root-servers.net (Verisign)
#     "193.0.14.129",    # k.root-servers.net (RIPE NCC)
#     "199.7.83.42",     # l.root-servers.net (ICANN)
#     "202.12.27.33",    # m.root-servers.net (WIDE)
# ]
# prime_tlds = [                                # TLDs to pre-warm on startup (recursive mode)
#     "com", "net", "org", "info",              # gTLDs
#     "io", "dev", "app", "xyz", "me",
#     "eu", "uk", "de", "fr", "nl",             # EU + European ccTLDs
#     "it", "es", "pl", "se", "no",
#     "dk", "fi", "at", "be", "ie",
#     "pt", "cz", "ro", "gr", "hu",
#     "bg", "hr", "sk", "si", "lt",
#     "lv", "ee", "ch", "is",
#     "co", "br", "au", "ca", "jp",             # other major ccTLDs
# ]

# [[forwarding]]                                  # per-suffix conditional forwarding rules
# suffix = "168.192.in-addr.arpa"                  # single suffix → one upstream
# upstream = "100.90.1.63:5361"
#
# [[forwarding]]
# suffix = ["home.local", "home.arpa"]             # multiple suffixes → same upstream
# upstream = "10.0.0.1"                            # port 53 default
#
# [[forwarding]]                                    # DoT upstream: tls://IP[:port]#hostname
# suffix = ["google.com", "goog"]                   # hostname is the TLS SNI / cert name
# upstream = "tls://9.9.9.9#dns.quad9.net"          # port 853 default
#
# [[forwarding]]                                    # DoH upstream: full https:// URL
# suffix = "example.corp"
# upstream = "https://dns.quad9.net/dns-query"
#
# [[forwarding]]                                    # array of upstreams → SRTT-aware failover
# suffix = ["google.com", "goog"]                   # fastest-healthy first, dead one skipped
# upstream = [
#     "tls://9.9.9.9#dns.quad9.net",
#     "tls://149.112.112.112#dns.quad9.net",
# ]

# [blocking]
# enabled = true               # set to false to disable ad blocking
# refresh_hours = 24
# lists = [
#     "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/hosts/pro.txt",
#     "file:///etc/numa/local-blocklist.txt",  # local hosts-style file
#     "/etc/numa/extra-blocks.txt",            # bare absolute path also works
# ]
# allowlist = ["example.com"]  # domains to never block

[cache]
max_entries = 100000
min_ttl = 60
max_ttl = 86400
# warm = ["google.com", "github.com"]  # resolve at startup, refresh before TTL expiry

[proxy]
enabled = true
port = 80
tls_port = 443
tld = "numa"
# bind_addr = "127.0.0.1"  # default; set to "0.0.0.0" for LAN access to .numa services

# PROXY protocol v2 — preserves the real client IP when numa runs behind an
# L4 front-end (dnsdist, HAProxy, nginx-stream, AWS NLB, GCP TCP LB). Empty
# `from` allowlist (or omitted section) leaves the feature disabled.
# A non-empty list puts the listener in PROXY-required mode: connections from
# listed senders that omit the header are dropped, and connections from
# non-listed senders are dropped before any read.
# [proxy.proxy_protocol]
# from = ["10.0.0.0/8", "127.0.0.1"]  # CIDR allowlist of trusted L4 front-ends
# header_timeout_ms = 5000             # ms; kill stalled-header connections fast

# Pre-configured services (numa.numa is always added automatically)
# [[services]]
# name = "frontend"
# target_port = 5173
#
# [[services]]
# name = "api"
# target_port = 8000

# Example zone records:
# [[zones]]
# domain = "dimescu.ro"
# record_type = "A"
# value = "3.120.139.105"
# ttl = 30

# [[zones]]
# domain = "test.local"
# record_type = "A"
# value = "127.0.0.1"
# ttl = 60

# Reverse-DNS (PTR) for LAN hosts — makes tcpdump/access logs readable
# [[zones]]
# domain = "1.0.168.192.in-addr.arpa"   # 192.168.0.1 reversed + .in-addr.arpa
# record_type = "PTR"
# value = "router.lan"
# ttl = 300

# Wildcard zones — one rule covers an entire subtree. Leftmost label only
# (RFC 4592). Exact entries always win; the wildcard does NOT match its parent
# (e.g. `*.pool.ntp.org` matches `time2.pool.ntp.org` but not `pool.ntp.org`).
# [[zones]]
# domain = "*.pool.ntp.org"
# record_type = "CNAME"
# value = "time.onsite"
# ttl = 60

# DNSSEC signature validation (requires mode = "recursive")
# [dnssec]
# enabled = false             # opt-in: verify chain of trust from root KSK
# strict = false              # true = SERVFAIL on bogus signatures

# DNS-over-TLS listener (RFC 7858) — encrypted DNS on port 853
# [dot]
# enabled = true              # on by default; set false to disable
# port = 853                  # standard DoT port
# bind_addr = "0.0.0.0"       # IPv4 or IPv6; unspecified binds all interfaces
# cert_path = "/etc/numa/dot.crt"  # PEM cert; omit to use self-signed (proxy CA if available)
# key_path = "/etc/numa/dot.key"   # PEM private key; must be set together with cert_path

# PROXY v2 also attaches to DoT — same semantics as [proxy.proxy_protocol].
# [dot.proxy_protocol]
# from = ["10.0.0.0/8"]
# header_timeout_ms = 5000

# LAN service discovery via mDNS (disabled by default — no network traffic unless enabled)
# [lan]
# enabled = true              # discover other Numa instances via mDNS (_numa._tcp.local)
# broadcast_interval_secs = 30
# peer_timeout_secs = 90

# Mobile API — persistent HTTP listener serving read-only routes
# (/health, /ca.pem, /mobileconfig, /ca.mobileconfig) on a LAN-reachable
# port. Consumed by the iOS/Android companion apps for discovery and
# profile fetching, and by `numa setup-phone` for QR-based onboarding.
#
# Opt-in because the listener binds to the LAN by default. None of the
# exposed routes are cryptographically sensitive (no private keys, no
# state mutations, all idempotent GETs), but enabling it does add a new
# listener to any device on the LAN that scans port 8765.
#
# Safe for home LANs. Think twice before enabling on untrusted LANs
# (office Wi-Fi, coffee shops, etc.) — an attacker on the same network
# could run a competing Numa instance that shadows yours via mDNS and
# trick companion apps into installing their profile instead of yours.
[mobile]
enabled = true                # opt-in to the mobile API listener
# port = 8765                 # default; matches Discovery.swift defaultAPIPort
# bind_addr = "0.0.0.0"       # default; set to "127.0.0.1" for localhost-only
