add local service proxy with .numa domains

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>
This commit is contained in:
Razvan Dimescu
2026-03-20 15:07:15 +02:00
parent 14a9e9e7e3
commit 8f959ce0a5
15 changed files with 762 additions and 53 deletions

View File

@@ -2,14 +2,15 @@
**DNS you own. Everywhere you go.**
Block ads and trackers. Override DNS for development. 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
- **Ad blocking that travels with you** — 385K+ domains blocked out of the box. Works on any network: coffee shops, hotels, airports.
- **Developer overrides** — point any hostname to any IP with auto-revert. No more editing `/etc/hosts`.
- **Local service proxy** — access `frontend.numa`, `api.numa` instead of `localhost:5173`. Clean URLs with WebSocket support for HMR.
- **Sub-millisecond caching** — cached lookups in 0ms. Faster than any public resolver.
- **Live dashboard** — real-time query stats, blocking controls, override management at `http://localhost:5380`.
- **Live dashboard** — real-time query stats, blocking controls, override management, local services at `http://numa.numa` (or `localhost:5380`).
- **Single binary, zero config** — just run it.
## Quick Start
@@ -32,7 +33,7 @@ docker run -p 53:53/udp -p 5380:5380 numa
### Try it
Open the dashboard: **http://localhost:5380**
Open the dashboard: **http://numa.numa** (or `http://localhost:5380`)
```bash
dig @127.0.0.1 google.com # ✓ resolves normally
@@ -59,18 +60,49 @@ curl -X POST http://localhost:5380/overrides \
dig @127.0.0.1 api.dev # → 127.0.0.1 (auto-reverts in 5 min)
```
## Local Service Proxy
Name your local dev services with `.numa` domains instead of remembering port numbers:
```bash
# Register a service via API
curl -X POST http://localhost:5380/services \
-H 'Content-Type: application/json' \
-d '{"name":"frontend","target_port":5173}'
# Now access it by name
open http://frontend.numa # → proxied to localhost:5173
```
Or configure in `numa.toml`:
```toml
[[services]]
name = "frontend"
target_port = 5173
[[services]]
name = "api"
target_port = 8000
```
- `numa.numa` is pre-configured — the dashboard itself, accessible without remembering the port
- WebSocket support — Vite/webpack HMR works through the proxy
- Health checks — dashboard shows green/red status for each service
- Manage via dashboard UI or REST API
## Resolution Pipeline
```
Query → Overrides → Blocklist → Local Zones → Cache → Upstream → Respond
Query → Overrides → .numa TLD → Blocklist → Local Zones → Cache → Upstream → Respond
```
1. **Overrides** — ephemeral, time-scoped redirects (highest priority)
2. **Blocklist**385K+ ad/tracker domains → returns `0.0.0.0` / `::`
3. **Local zones** — records defined in `[[zones]]` config
4. **Cache** — TTL-adjusted cached upstream responses (sub-ms)
5. **Forward**query upstream resolver, cache the result
6. **SERVFAIL** — returned on upstream failure
2. **`.numa` TLD** — synthetic domains for local services → returns `127.0.0.1`
3. **Blocklist** — 385K+ ad/tracker domains → returns `0.0.0.0` / `::`
4. **Local zones** — records defined in `[[zones]]` config
5. **Cache**TTL-adjusted cached upstream responses (sub-ms)
6. **Forward** — query upstream resolver, cache the result
7. **SERVFAIL** — returned on upstream failure
## Dashboard
@@ -80,6 +112,7 @@ Live at `http://localhost:5380` when Numa is running:
- Resolution path breakdown (forward / cached / local / override / blocked)
- Scrolling query log with colored path tags
- Active overrides with create/edit/delete
- Local services with health status and add/remove
- Blocking controls: toggle on/off, pause 5 minutes, one-click allowlist
- Cached domains list
@@ -110,6 +143,15 @@ lists = [
refresh_hours = 24
allowlist = []
[proxy]
enabled = true
port = 80
tld = "numa"
[[services]]
name = "frontend"
target_port = 5173
[[zones]]
domain = "mysite.local"
record_type = "A"
@@ -119,7 +161,7 @@ ttl = 60
## HTTP API
REST API on port 5380 (18 endpoints):
REST API on port 5380 (22 endpoints):
| Endpoint | Method | Description |
|----------|--------|-------------|
@@ -130,12 +172,16 @@ REST API on port 5380 (18 endpoints):
| `/overrides/environment` | POST | Batch load overrides |
| `/overrides/{domain}` | GET | Get specific override |
| `/overrides/{domain}` | DELETE | Remove specific override |
| `/services` | GET | List local services (with health status) |
| `/services` | POST | Register a local service |
| `/services/{name}` | DELETE | Remove a local service |
| `/blocking/stats` | GET | Blocklist stats (domains loaded, sources, enabled) |
| `/blocking/toggle` | PUT | Enable/disable blocking |
| `/blocking/pause` | POST | Pause blocking for N minutes |
| `/blocking/allowlist` | GET | List allowlisted domains |
| `/blocking/allowlist` | POST | Add domain to allowlist |
| `/blocking/allowlist/{domain}` | DELETE | Remove from allowlist |
| `/blocking/check/{domain}` | GET | Check if domain is blocked |
| `/diagnose/{domain}` | GET | Trace resolution path |
| `/query-log` | GET | Recent queries (filterable) |
| `/stats` | GET | Server statistics |
@@ -151,6 +197,7 @@ REST API on port 5380 (18 endpoints):
| Ad blocking | Yes | Yes | Limited | 385K+ domains |
| Portable | No (Raspberry Pi) | Cloud only | Cloud only | Single binary |
| Developer overrides | No | No | No | REST API + auto-expiry |
| Local service proxy | No | No | No | `.numa` domains + WebSocket |
| Data stays local | Yes | Cloud | Cloud | 100% local |
| Zero config | Complex setup | Yes | Yes | Works out of the box |
| Self-sovereign DNS | No | No | No | pkarr/DHT roadmap |
@@ -159,6 +206,8 @@ REST API on port 5380 (18 endpoints):
**Block ads everywhere** — Run Numa on your laptop. Your ad blocker works on any network.
**Name your local services**`frontend.numa` instead of `localhost:5173`. CORS-friendly, HMR-compatible.
**Mock external services**`Point api.stripe.com to localhost:8080 for 30 minutes`
**Provision dev environments** — Create overrides for `db.dev`, `api.dev`, `cache.dev`
@@ -176,6 +225,7 @@ Zero external DNS libraries. RFC 1035 wire protocol parsed by hand. Dependencies
- [x] Ad blocking — 385K+ domains, dashboard, allowlist
- [x] System DNS auto-discovery — Tailscale, VPN split-DNS
- [x] System DNS auto-configuration — `numa install` / `numa uninstall`
- [x] Local service proxy — `.numa` domains with HTTP reverse proxy + WebSocket support
- [ ] pkarr integration — self-sovereign DNS via Mainline DHT
- [ ] Decentralized resolver network — staking, auditing, token economics