[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 docs/implementation/ # # bootstrap-resolver.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"] # 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 # 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 # 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 # 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