* feat: auto recursive mode, fix Linux install
Auto mode (new default): probes a root server on startup; uses
recursive resolution if outbound DNS works, falls back to Quad9 DoH
if blocked. Dashboard shows mode indicator (green/yellow).
Linux install fixes:
- Add DNSStubListener=no to resolved drop-in (frees port 53)
- Configure DNS before starting service (correct ordering)
- Skip 127.0.0.53 in upstream detection
- `numa install` now does everything (service + DNS + CA)
- `numa uninstall` mirrors install (stop service + restore DNS)
- Extract is_loopback_or_stub() for consistent filtering
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: enable DNSSEC validation by default
With recursive as the default mode, DNSSEC validation completes the
trustless resolution chain. Strict mode remains off by default.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: forward search domains to VPC resolver on Linux
Parse search/domain lines from resolv.conf and create conditional
forwarding rules to the original nameserver or AWS VPC resolver
(169.254.169.253). Fixes internal hostname resolution on cloud VMs
where recursive mode can't resolve private DNS zones.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: single-pass resolv.conf parsing, eliminate redundancies
Parse resolv.conf once for both upstream and search domains instead
of 2-3 reads. Extract CLOUD_VPC_RESOLVER constant. Use &'static str
for mode in StatsResponse. Remove dead read_upstream_from_file.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: macOS install health check, harden recursive probe
Verify numa is listening (API port) before redirecting system DNS on
macOS — if the service fails to start (e.g. port 53 in use), unload
the service and abort instead of breaking DNS. Probe up to 3 root
hints before declaring recursive mode unavailable. Validate IPs from
resolvectl to avoid IPv6 fragment extraction. Extract DEFAULT_API_PORT
constant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: widen make_rule cfg gate to include Linux
make_rule was gated to macOS-only but discover_linux() calls it for
search domain forwarding rules. CI failed on Linux with E0425.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: forward mode as default, recursive opt-in
Forward mode (transparent proxy to system DNS) is now the default.
Recursive and auto modes are explicit opt-in via config. This avoids
bypassing corporate DNS policies, captive portals, VPC private zones,
and parental controls on first install.
- Move #[default] from Auto to Forward on UpstreamMode
- DNSSEC defaults to off (no-op in forward mode)
- 3-way match in main: Forward/Recursive/Auto with clean separation
- Post-install message suggests mode = "recursive" for sovereignty
- Update README, site, and launch drafts messaging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: SRTT-based nameserver selection for recursive resolver
BIND-style Smoothed RTT (EWMA) tracking per NS IP address. The resolver
learns which nameservers respond fastest and prefers them, eliminating
cascading timeouts from slow/unreachable IPv6 servers.
- New src/srtt.rs: SrttCache with record_rtt, record_failure, sort_by_rtt
- EWMA formula: new = (old * 7 + sample) / 8, 5s failure penalty, 5min decay
- TCP penalty (+100ms) lets SRTT naturally deprioritize IPv6-over-TCP
- Enabled flag embedded in SrttCache (no-op when disabled)
- Batch eviction (64 entries) for O(1) amortized writes at capacity
- Configurable via [upstream] srtt = true/false (default: true)
- Benchmark script: scripts/benchmark.sh (full, cold, warm, compare-all)
- Benchmarks show 12x avg improvement, 0% queries >1s (was 58%)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: show DNSSEC and SRTT status in dashboard + API
Add dnssec and srtt boolean fields to /stats API response.
Display on/off indicators in the dashboard footer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: apply SRTT decay before EWMA so recovered servers rehabilitate
Without decay-before-EWMA, a server penalized at 5000ms stayed near
that value even after recovery — the stale raw penalty was used as the
EWMA base instead of the decayed estimate. Extract decayed_srtt()
helper and call it in record_rtt() before the smoothing step.
Also restores removed "why" comments in send_query / resolve_recursive.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add install/upgrade instructions, smarter benchmark priming
README: document `numa install`, `numa service`, Homebrew upgrade,
and `make deploy` workflows. Benchmark: replace fixed `sleep 4` with
`wait_for_priming` that polls cache entry count for stability.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: DNS-over-HTTPS upstream forwarding
Encrypt upstream queries via DoH — ISPs see HTTPS traffic on port 443,
not plaintext DNS on port 53. URL scheme determines transport:
https:// = DoH, bare IP = plain UDP. Falls back to Quad9 DoH when
system resolver cannot be detected.
- Upstream enum (Udp/Doh) with Display and PartialEq
- BytePacketBuffer::from_bytes constructor
- reqwest http2 feature for DoH server compatibility
- network_watch_loop guards against DoH→UDP silent downgrade
- 5 new tests (mock DoH server, HTTP errors, timeout)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* style: cargo fmt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add DoH to README — Why Numa, comparison table, roadmap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Comparison table and "Why Numa" reordered so unique capabilities (service proxy,
path routing, LAN discovery) appear first. Added brew/cargo install to Quick Start.
Removed unshipped "Self-sovereign DNS" row from comparison table. Named hickory-dns
and trust-dns in "How It Works" to signal deliberate architectural choice.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add LAN discovery section to README with mesh and hub mode docs.
Update comparison table and roadmap. Move Local Services panel
above Blocking in dashboard for developer-first layout.
Bump version from 0.1.0 to 0.3.0 to match release cadence.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Roman Stone themed 404 with Instrument Serif heading, JetBrains Mono
domain badge, brick pattern background, syntax-highlighted curl
example, and a delayed easter egg. Also updates dashboard link in
README to numa.numa.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Docs contain internal planning (token economics, growth plan) that
should not be public. Keep locally via .gitignore.
Added macOS ad-hoc codesign step to install.sh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Moved "from scratch in Rust" into hero, added AdGuard Home to
comparison, named Hagezi Pro blocklist, cut 40% (API table + config
to docs), install script first in Quick Start, added Linux mention.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Set TC (truncation) bit when response exceeds 4096-byte buffer
instead of dropping the response silently. Clients can retry via TCP.
- Log when upstream response is truncated in forward.rs.
- Dockerfile: bump to Rust 1.88, include site/service files, use
alpine runtime instead of scratch, add cmake/perl for aws-lc-sys.
- Makefile deploy: platform-aware — codesign on macOS, systemctl on Linux.
- README: trim roadmap to near-term items only.
- Verified: Docker build + smoke test passes on Linux (Alpine musl).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
security delete-certificate -c fails when multiple certs match.
Now finds all certs by hash and deletes each individually.
Also updated README with HTTPS, service persistence, and TLS mentions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HTTP reverse proxy on port 80 lets developers use clean domain names
(frontend.numa, api.numa) instead of localhost:PORT. Includes WebSocket
upgrade support for HMR, TCP health checks, dashboard UI panel, and
REST API for service management. numa.numa is preconfigured for the
dashboard itself.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- DNS-level ad blocking: 385K+ domains via Hagezi Pro blocklist, subdomain
matching, one-click allowlist, pause/toggle, background refresh every 24h
- Live dashboard at :5380 with real-time stats, query log, override
management (create/edit/delete), blocking controls
- System DNS auto-discovery: parses scutil --dns on macOS to find
conditional forwarding rules (Tailscale, VPN split-DNS)
- REST API expanded to 18 endpoints (blocking, overrides, diagnostics)
- Startup banner with colored system info
- Performance benchmarks (bench/dns-bench.sh)
- Landing page updated with new positioning and comparison table
- CI, Dockerfile, LICENSE, development plan docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>