Merge pull request #4 from razvandimescu/feat/network-economics
Add hero GIF, network economics doc, updated roadmap
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
docs/
|
|
||||||
|
|||||||
113
CLAUDE.md
113
CLAUDE.md
@@ -1,113 +0,0 @@
|
|||||||
# CLAUDE.md
|
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
**Numa** — a portable DNS resolver with ad blocking, developer overrides, and a live dashboard. Built from scratch in Rust. Named after Numa Pompilius, the Roman king who established lasting institutions.
|
|
||||||
|
|
||||||
Today: DNS forwarding/caching proxy with ad blocking, ephemeral overrides, live dashboard, and system DNS integration.
|
|
||||||
Next: Self-sovereign DNS via pkarr/Mainline DHT.
|
|
||||||
Vision: Incentivized resolver network with staking, challenge-based auditing, and token economics.
|
|
||||||
|
|
||||||
## Build & Run
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo build # compile
|
|
||||||
sudo cargo run # run with default config (numa.toml)
|
|
||||||
sudo cargo run -- path/to/config # run with custom config path
|
|
||||||
RUST_LOG=debug sudo cargo run # verbose logging
|
|
||||||
make lint # clippy + rustfmt check
|
|
||||||
```
|
|
||||||
|
|
||||||
Test with: `dig @127.0.0.1 google.com`
|
|
||||||
|
|
||||||
CLI commands:
|
|
||||||
```bash
|
|
||||||
numa help # show all commands
|
|
||||||
numa install # set system DNS to 127.0.0.1
|
|
||||||
numa uninstall # restore original DNS
|
|
||||||
numa service start # install as persistent service (launchd/systemd)
|
|
||||||
numa service stop # uninstall service + restore DNS
|
|
||||||
numa service status # check service status
|
|
||||||
```
|
|
||||||
|
|
||||||
Dashboard: http://numa.numa (or http://localhost:5380)
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
UDP :53 ──▶ handle_query()
|
|
||||||
│
|
|
||||||
├─ 1. Override Store (ephemeral, auto-expiry)
|
|
||||||
├─ 2. .numa TLD (local service domains → 127.0.0.1)
|
|
||||||
├─ 3. Blocklist (385K+ domains, subdomain matching)
|
|
||||||
├─ 4. Local Zones (TOML config)
|
|
||||||
├─ 5. Cache (TTL-aware, lazy eviction)
|
|
||||||
└─ 6. Upstream Forward (auto-detected from OS, conditional forwarding)
|
|
||||||
|
|
||||||
HTTP :80 ──▶ Reverse proxy for .numa domains (WebSocket support)
|
|
||||||
HTTPS :443 ──▶ TLS reverse proxy (auto-generated local CA + wildcard *.numa cert)
|
|
||||||
HTTP :5380 ──▶ Axum REST API (22+ endpoints) + Dashboard
|
|
||||||
```
|
|
||||||
|
|
||||||
### Source Files
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
main.rs # startup: load config, bind UDP, spawn API + proxy, blocklist download, per-query task loop
|
|
||||||
lib.rs # module declarations, Error/Result type aliases
|
|
||||||
ctx.rs # ServerCtx shared state + handle_query() pipeline
|
|
||||||
api.rs # Axum REST server (22 endpoints, port 5380) + embedded dashboard
|
|
||||||
config.rs # TOML config loading with defaults (server, upstream, cache, blocking, proxy, zones)
|
|
||||||
proxy.rs # HTTP/HTTPS reverse proxy for .numa domains (port 80 + 443, WebSocket upgrade)
|
|
||||||
tls.rs # Local CA + wildcard cert generation (rcgen), rustls ServerConfig builder
|
|
||||||
service_store.rs # ServiceStore — name-to-port mappings, persisted to ~/.config/numa/services.json
|
|
||||||
blocklist.rs # BlocklistStore — HashSet<String>, download, parse, subdomain matching, check
|
|
||||||
override_store.rs # OverrideStore — ephemeral domain overrides with auto-expiry
|
|
||||||
query_log.rs # ring buffer (VecDeque, 1000 entries) for recent queries
|
|
||||||
cache.rs # DnsCache — TTL-aware, lazy eviction every 1000 lookups
|
|
||||||
forward.rs # async UDP forwarding to upstream resolver
|
|
||||||
stats.rs # ServerStats counters + QueryPath enum (6 categories)
|
|
||||||
system_dns.rs # OS DNS discovery (scutil/resolv.conf), install/uninstall, service management
|
|
||||||
buffer.rs # BytePacketBuffer — 4096-byte DNS wire format I/O
|
|
||||||
header.rs # DnsHeader — 12-byte bitfield parsing/serialization
|
|
||||||
question.rs # DnsQuestion + QueryType enum (A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, SRV, HTTPS)
|
|
||||||
record.rs # DnsRecord enum — wire format read/write per record type (filters UNKNOWN on write)
|
|
||||||
packet.rs # DnsPacket — header + questions + answers + authorities + resources
|
|
||||||
site/
|
|
||||||
dashboard.html # live dashboard (embedded at compile time via include_str!)
|
|
||||||
index.html # landing page (Roman Stone theme)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Config
|
|
||||||
|
|
||||||
`numa.toml` at project root. Sections: `[server]`, `[upstream]`, `[cache]`, `[blocking]`, `[proxy]`, `[[services]]`, `[[zones]]`. Falls back to sensible defaults if file is missing. Upstream auto-detected from system resolver if not set.
|
|
||||||
|
|
||||||
## REST API
|
|
||||||
|
|
||||||
Dashboard: GET `/` (embedded HTML)
|
|
||||||
Override management: POST/GET/DELETE `/overrides`, POST `/overrides/environment`
|
|
||||||
Services: GET/POST `/services`, DELETE `/services/{name}`
|
|
||||||
Blocking: GET `/blocking/stats`, PUT `/blocking/toggle`, POST `/blocking/pause`, POST `/blocking/unpause`, GET/POST `/blocking/allowlist`, GET `/blocking/check/{domain}`
|
|
||||||
Diagnostics: GET `/diagnose/{domain}`, `/query-log`, `/stats`, `/cache`, `/health`
|
|
||||||
Cache: DELETE `/cache`, `/cache/{domain}`
|
|
||||||
|
|
||||||
## Key Details
|
|
||||||
|
|
||||||
- Rust 2021 edition, async via `tokio` (rt-multi-thread)
|
|
||||||
- Deps: tokio, axum, hyper, hyper-util, serde, serde_json, toml, log, env_logger, reqwest, futures, rcgen, rustls, tokio-rustls, time (zero DNS libraries)
|
|
||||||
- Shared config dir: `~/.config/numa/` via `config_dir()` in `lib.rs` (handles sudo correctly)
|
|
||||||
- TLS: auto-generated local CA + wildcard `*.numa` cert at `~/.config/numa/`. `numa install` trusts CA in OS keychain.
|
|
||||||
- Service persistence: user-added services saved to `~/.config/numa/services.json`, survives restarts
|
|
||||||
- Deploy workflow: `make deploy` (build release → copy → codesign → kill → launchd respawns)
|
|
||||||
- DNS buffer size: 4096 bytes (EDNS-compatible). UNKNOWN record types (e.g. OPT) filtered on serialization.
|
|
||||||
- `BytePacketBuffer::read_qname` handles label compression (pointer jumps)
|
|
||||||
- `type Error = Box<dyn std::error::Error + Send + Sync>` / `type Result<T>` aliased in `lib.rs`
|
|
||||||
- Shared state via `Arc<ServerCtx>` with `std::sync::Mutex` (sub-microsecond holds, never across `.await`)
|
|
||||||
- Cache: TTL clamped between `min_ttl` and `max_ttl`, lazy eviction every 1000 queries
|
|
||||||
- Blocklist: parsed outside lock, swapped atomically. `is_blocked()` takes `&self` (read-only).
|
|
||||||
- Upstream: auto-detected from `scutil --dns` (macOS) or `/etc/resolv.conf` (Linux). Falls back to Quad9.
|
|
||||||
- Conditional forwarding: Tailscale/VPN domains auto-routed to correct upstream.
|
|
||||||
- macOS service: launchd plist with KeepAlive + RunAtLoad. Use `launchctl bootstrap/bootout` (not load/unload).
|
|
||||||
- Logging controlled via `RUST_LOG` env var. Default: `info`
|
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
Block ads and trackers. Override DNS for development. Name your local services. Cache for speed. A single portable binary built from scratch in Rust — no Raspberry Pi, no cloud, no account.
|
Block ads and trackers. Override DNS for development. Name your local services. Cache for speed. A single portable binary built from scratch in Rust — no Raspberry Pi, no cloud, no account.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Why
|
## Why
|
||||||
|
|
||||||
- **Ad blocking that travels with you** — 385K+ domains blocked out of the box. Works on any network: coffee shops, hotels, airports.
|
- **Ad blocking that travels with you** — 385K+ domains blocked out of the box. Works on any network: coffee shops, hotels, airports.
|
||||||
@@ -228,8 +230,11 @@ Zero external DNS libraries. RFC 1035 wire protocol parsed by hand. Dependencies
|
|||||||
- [x] System DNS auto-discovery — Tailscale, VPN split-DNS
|
- [x] System DNS auto-discovery — Tailscale, VPN split-DNS
|
||||||
- [x] System DNS auto-configuration — `numa install` / `numa uninstall`
|
- [x] System DNS auto-configuration — `numa install` / `numa uninstall`
|
||||||
- [x] Local service proxy — `.numa` domains with HTTP/HTTPS reverse proxy, auto TLS, WebSocket
|
- [x] Local service proxy — `.numa` domains with HTTP/HTTPS reverse proxy, auto TLS, WebSocket
|
||||||
- [ ] pkarr integration — self-sovereign DNS via Mainline DHT
|
- [ ] pkarr integration — resolve Ed25519 keys via Mainline DHT (15M nodes)
|
||||||
- [ ] Decentralized resolver network — staking, auditing, token economics
|
- [ ] Global `.numa` names — self-publish, DHT-backed, first-come-first-served
|
||||||
|
- [ ] Audit protocol — challenge-based verification of resolver honesty
|
||||||
|
- [ ] Numa Network — proof-of-service consensus, NUMA token, paid `.numa` domains
|
||||||
|
- [ ] `.onion` bridge — human-readable `.numa` names for Tor hidden services
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
BIN
assets/hero-demo.gif
Normal file
BIN
assets/hero-demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 808 KiB |
@@ -1357,27 +1357,27 @@ footer .closing {
|
|||||||
</div>
|
</div>
|
||||||
<div class="roadmap-item done">
|
<div class="roadmap-item done">
|
||||||
<span class="phase">Phase 4</span>
|
<span class="phase">Phase 4</span>
|
||||||
<span class="phase-desc">Local service proxy — .numa domains, HTTP reverse proxy, WebSocket support</span>
|
<span class="phase-desc">Local service proxy — .numa domains, HTTP/HTTPS reverse proxy, auto TLS, WebSocket</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="roadmap-item phase-teal">
|
<div class="roadmap-item phase-teal">
|
||||||
<span class="phase">Phase 5</span>
|
<span class="phase">Phase 5</span>
|
||||||
<span class="phase-desc">pkarr spike — DHT resolution and publish endpoint</span>
|
<span class="phase-desc">pkarr integration — resolve Ed25519 keys via Mainline DHT (15M nodes)</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="roadmap-item phase-teal">
|
<div class="roadmap-item phase-teal">
|
||||||
<span class="phase">Phase 6</span>
|
<span class="phase">Phase 6</span>
|
||||||
<span class="phase-desc">pkarr product — human-readable aliases, re-publish daemon, key management</span>
|
<span class="phase-desc">Global .numa names — self-publish, DHT-backed, first-come-first-served</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="roadmap-item phase-amber">
|
<div class="roadmap-item phase-amber">
|
||||||
<span class="phase">Phase 7</span>
|
<span class="phase">Phase 7</span>
|
||||||
<span class="phase-desc">Challenge and audit protocol for verifiable resolver behavior</span>
|
<span class="phase-desc">Audit protocol — challenge-based verification of resolver honesty</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="roadmap-item phase-violet">
|
<div class="roadmap-item phase-violet">
|
||||||
<span class="phase">Phase 8</span>
|
<span class="phase">Phase 8</span>
|
||||||
<span class="phase-desc">Token economics, staking, and slashing mechanism</span>
|
<span class="phase-desc">Numa Network — proof-of-service consensus, NUMA token, paid .numa domains</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="roadmap-item phase-violet">
|
<div class="roadmap-item phase-violet">
|
||||||
<span class="phase">Phase 9</span>
|
<span class="phase">Phase 9</span>
|
||||||
<span class="phase-desc">Decentralized resolver marketplace</span>
|
<span class="phase-desc">.onion bridge — human-readable .numa names for Tor hidden services</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user