From cfd9a562af42f1d48109a81bce0e4335d74f6b8a Mon Sep 17 00:00:00 2001 From: Razvan Dimescu Date: Sat, 21 Mar 2026 01:35:14 +0200 Subject: [PATCH] fix CA removal: delete by SHA-1 hash, update README with TLS 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 --- README.md | 8 +++++--- src/system_dns.rs | 33 +++++++++++++++++++++++++++------ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index af4a118..aa44da2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Block ads and trackers. Override DNS for development. Name your local services. - **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. +- **Local service proxy** — access `https://frontend.numa` instead of `localhost:5173`. Auto-generated TLS certs, 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, local services at `http://numa.numa` (or `localhost:5380`). - **Single binary, zero config** — just run it. @@ -86,8 +86,10 @@ target_port = 8000 ``` - `numa.numa` is pre-configured — the dashboard itself, accessible without remembering the port +- **HTTPS with green lock** — auto-generated local CA + per-service TLS certs. `sudo numa install` trusts the CA in your system keychain. - WebSocket support — Vite/webpack HMR works through the proxy - Health checks — dashboard shows green/red status for each service +- Services persist across restarts (`~/.config/numa/services.json`) - Manage via dashboard UI or REST API ## Resolution Pipeline @@ -197,7 +199,7 @@ REST API on port 5380 (22 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 | +| Local service proxy | No | No | No | `.numa` domains + HTTPS + 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 | @@ -225,7 +227,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 +- [x] Local service proxy — `.numa` domains with HTTP/HTTPS reverse proxy, auto TLS, WebSocket - [ ] pkarr integration — self-sovereign DNS via Mainline DHT - [ ] Decentralized resolver network — staking, auditing, token economics diff --git a/src/system_dns.rs b/src/system_dns.rs index 31a942a..6b63c48 100644 --- a/src/system_dns.rs +++ b/src/system_dns.rs @@ -820,13 +820,34 @@ fn untrust_ca() -> Result<(), String> { #[cfg(target_os = "macos")] { - if ca_path.exists() { - let _ = std::process::Command::new("security") - .args(["remove-trusted-cert", "-d"]) - .arg(&ca_path) - .status(); - eprintln!(" Removed Numa CA from system keychain"); + // Find all Numa CA certs by hash and delete each one + if let Ok(out) = std::process::Command::new("security") + .args([ + "find-certificate", + "-c", + "Numa Local CA", + "-a", + "-Z", + "/Library/Keychains/System.keychain", + ]) + .output() + { + let stdout = String::from_utf8_lossy(&out.stdout); + for line in stdout.lines() { + if let Some(hash) = line.strip_prefix("SHA-1 hash: ") { + let hash = hash.trim(); + let _ = std::process::Command::new("security") + .args([ + "delete-certificate", + "-Z", + hash, + "/Library/Keychains/System.keychain", + ]) + .output(); + } + } } + eprintln!(" Removed Numa CA from system keychain"); } #[cfg(target_os = "linux")]