Numa

DNS you own. Everywhere you go.

After Numa Pompilius, who built institutions that outlasted kings.

Block ads and trackers. Override DNS for development. Name your local services with .numa domains. A single portable binary built from scratch in Rust — no Raspberry Pi, no cloud, no account.

DNS is a single point of control

Every time you visit a website, you ask a DNS resolver where to go. That resolver sees every domain you visit, when, and how often. Your ISP logs these queries by default.

Ad blockers work in one browser. Pi-hole needs a Raspberry Pi. Your local dev services live at localhost:5173 and you can never remember which port is which.

DNS is the foundation of everything you do on the internet, but the tools for controlling it locally are either too complex (dnsmasq + nginx + mkcert) or too limited (cloud-only, appliance-only).

Your browser
Your ISP / OS resolver
|
Single point of failure
Cloudflare 1.1.1.1 / Google 8.8.8.8
|
ICANN root servers
TLD registrars (.com, .io, ...)
Authoritative nameservers

What it does today

A portable DNS proxy with ad blocking, encrypted upstream, local service domains, and a REST API. Everything runs in a single binary.

Layer 1

Block & Protect

  • Ad & tracker blocking — 385K+ domains, zero config
  • DNS-over-HTTPS — encrypted upstream (Quad9, Cloudflare, any provider)
  • TTL-aware caching (sub-ms lookups)
  • Single binary, portable — your DNS travels with you
  • macOS, Linux, and Windows
Layer 2

Developer Tools

  • Local service proxy — frontend.numa instead of localhost:5173
  • Path-based routing — app.numa/api:5001
  • Ephemeral DNS overrides with auto-revert
  • LAN service discovery via mDNS
  • Conditional forwarding — plays nice with Tailscale/VPN split-DNS
  • REST API — script everything, automate anything
  • Live dashboard with real-time stats and controls
Coming Next

Self-Sovereign DNS

  • pkarr integration — DNS via Mainline DHT, no registrar needed
  • Global .numa names — self-publish, DHT-backed
  • .onion bridge — human-readable names for Tor hidden services
  • Ed25519 same-key binding — zero new trust assumptions
  • No blockchain required for core naming

Resolution pipeline

Every query walks through the same deterministic pipeline. Local data takes priority; the network is the fallback.

Query
Overrides
.numa TLD
Blocklist
Local Zones
Cache
DoH Upstream
Respond

Why Numa is different

Comparison of Numa with existing DNS solutions
Pi-hole NextDNS Cloudflare AdGuard Home Numa
Ad & tracker blocking Yes Yes Limited Yes 385K+ domains
Portable (travels with laptop) No (Raspberry Pi) Cloud only Cloud only No (network appliance) Single binary
Developer overrides No No No No REST API + auto-expiry
Local service proxy No No No No .numa domains + WebSocket
Data stays local Yes Cloud Cloud Yes 100% local
Live dashboard Yes Yes No Yes Real-time + controls
DNS-over-HTTPS upstream No Yes Yes No Built in (HTTP/2 + rustls)
Conditional forwarding No No No Manual Auto-detects Tailscale/VPN
Zero config needed Complex setup Yes Yes Docker/setup Works out of the box

Measured, not claimed

Benchmarked with dig against public resolvers on the same machine. Cached queries resolve in under a microsecond.

DNS resolver latency comparison
Resolver Avg P50 P99
Numa (cached) <1ms <1ms <1ms
Numa (cold) 9ms 9ms 18ms
System resolver 9ms 8ms 44ms
Quad9 15ms 13ms 43ms
Cloudflare 19ms 14ms 132ms
Google 22ms 17ms 37ms
Numa
<1ms
System
9ms
Quad9
15ms
Cloudflare
19ms
Google
22ms
689 ns
Cached round-trip — parse query, cache lookup, serialize response
2.0M
Queries per second (single-threaded pipeline throughput, batched)
0 allocations
Heap allocations in the I/O path — 4KB stack buffers, inline serialization

Cold queries match system resolver speed — the bottleneck is upstream RTT, not Numa. We don't claim to be faster when the network is the limit.

Benchmarks are reproducible: cargo bench for micro-benchmarks, python3 bench/dns-bench.sh for end-to-end. Methodology →

Technical details

Runtime
Rust + tokio async (rt-multi-thread)
DNS Libraries
Zero — wire protocol parsed from scratch
Dependencies
18 runtime crates — tokio, axum, hyper, reqwest (DoH), rcgen + rustls (TLS), socket2 (multicast), serde, and more
Packet Format
RFC 1035 compliant, 4096-byte UDP (EDNS)
Concurrency
Arc<ServerCtx> + RwLock for reads, Mutex for writes (never across .await)
Upstream
DNS-over-HTTPS (DoH) via reqwest + http2 + rustls
# Install (pick one) $ brew install razvandimescu/tap/numa $ cargo install numa $ curl -fsSL https://raw.githubusercontent.com/razvandimescu/numa/main/install.sh | sh # Run $ sudo numa # bind to :53, :80, :5380 $ dig @127.0.0.1 google.com # test resolution $ open http://localhost:5380 # dashboard $ curl -X POST localhost:5380/services \ -d '{"name":"frontend", "target_port":5173}' # https://frontend.numa

Where we're going

Phase 0 DNS proxy core — zones, caching, forwarding, async tokio runtime
Phase 1 Override layer + REST API for programmatic DNS control
Phase 2 Ad & tracker blocking — 385K+ domains, live dashboard, one-click allowlist
Phase 3 System integration — auto-discovery of OS DNS routing, one-command install
Phase 4 Local service proxy — .numa domains, HTTP/HTTPS reverse proxy, auto TLS, WebSocket
Phase 5 DNS-over-HTTPS — encrypted upstream, HTTP/2 connection pooling
Phase 6 pkarr integration — self-sovereign DNS via Mainline DHT, no registrar needed
Phase 7 Global .numa names — self-publish, DHT-backed, first-come-first-served
Phase 8 .onion bridge — human-readable Tor naming via Ed25519 same-key binding