Highlights
🔐 Dashboard & API authentication
The dashboard and REST API now require a token for any non-loopback client — HTTP Bearer or Basic (native browser prompt). No config needed: a token is minted on first start, logged once, and persisted with 0600 perms, so no deployment is ever unauthenticated. Set your own via [server] api_token or NUMA_API_TOKEN; /health stays open for probes. The .numa reverse proxy forwards the real client IP, so proxied dashboard access gets the same check. (#315)
Upgrading with a dashboard already exposed beyond localhost? Your next visit will ask for credentials — the token is in the startup log (printed once) and in api_token inside your data directory (e.g. /var/lib/numa/, /usr/local/var/numa/). Use it as the password with any username, or set your own in numa.toml.
🔏 DNSSEC: record types that were never actually verified
With [dnssec] enabled = true, answers for types Numa stores as raw rdata — TXT, HTTPS, SVCB, SRV, NAPTR, LOC — were reported Secure without any signature verification: their rrsets never matched their RRSIGs, so validation silently skipped them. They are now signature-checked like everything else, including RFC 4034 canonical-form downcasing for SRV/NAPTR targets. DNSSEC validation is opt-in, so only [dnssec] users were affected. (#325, closes #324)
🔗 CNAME chase correctness
Multi-link CNAME chains included in a single upstream response are now followed to the end before sub-querying, chased answers no longer duplicate chain links, and the chase terminates cleanly on record types without native parsing. (#323)
Also notable
- The wire parser is now continuously fuzzed in CI (4 targets, seed corpus), and the repo gained a
SECURITY.mddisclosure policy (#319) - Parser hardening: DNSKEY/DS records with rdlength shorter than their fixed fields are rejected (found by the fuzzer, #301); OPT records whose owner name is a compression pointer are recognized (#319)
- Per-client
filter_aaaaoverride in[[client_policy]](#307, closes #286) - Query-log entries carry a monotonic
seq, so external scrapers (numa-metrics) can page reliably (#310) - Forward mode re-resolves an undetectable system DNS to DoH on network rescan (#321, closes #169)
- Dashboard query log fills its column height (#302); AUR publishes are serialized (#298)
- Dependency security bumps: quinn-proto (RUSTSEC-2026-0185), crossbeam-epoch (RUSTSEC-2026-0204)
All changes
- fix(ci): serialize AUR publish runs by @razvandimescu in #298
- fix(dashboard): query log fills column height instead of leaving a void by @razvandimescu in #302
- fix(parser): reject DNSKEY/DS records with rdlength < fixed fields by @razvandimescu in #301
- site: publish numa services over tailnet by @razvandimescu in #304
- site: forgot image by @razvandimescu in #305
- chore: updated readme.md and index.html by @razvandimescu in #306
- feat(client_policy): per-client filter_aaaa override (#286) by @razvandimescu in #307
- docs: clarify Numa has no separate PROXY protocol port by @razvandimescu in #308
- feat(query-log): stamp a monotonic seq on each entry by @razvandimescu in #310
- docs(readme): link numa-metrics for durable query history by @razvandimescu in #311
- chore(deps): bump reqwest to 0.13, pin ring via rustls-no-provider by @razvandimescu in #312
- chore(deps): bump the minor-and-patch group with 7 updates by @dependabot[bot] in #318
- chore(deps): bump actions/checkout from 6 to 7 by @dependabot[bot] in #317
- chore(deps): bump alpine from 3.23 to 3.24 in the minor-and-patch group by @dependabot[bot] in #316
- Add SECURITY.md and CI fuzzing over the wire parser by @razvandimescu in #319
- fix(forward): resolve undetectable system DNS to DoH on rescan (#169) by @razvandimescu in #321
- test(integration): reap the resolver on exit, stop set -e hiding fail… by @razvandimescu in #322
- feat(api): authenticate the HTTP control plane by @razvandimescu in #315
- fix(ctx): stop duplicating CNAME chain links in chased answers by @razvandimescu in #323
- fix(dnssec): verify rrsets of types without a DnsRecord variant by @razvandimescu in #325
Full Changelog: v0.21.0...v0.22.0