From 42834b8008509450fd66fce1364be1765a7de072 Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Mon, 13 Apr 2026 05:34:51 +0000 Subject: [PATCH 1/5] Fix auto-connect reconnect on graceful peer disconnect handle_disconnect() called remove_active_peer without scheduling a reconnect, orphaning auto-connect peers on a clean upstream shutdown. Mirror the pattern from the other three peer-removal paths (link-dead, decrypt failure, peer restart) which all schedule reconnect after removal. Adds test_disconnect_schedules_reconnect regression test that verifies handle_disconnect populates retry_pending for an auto-connect peer. Visibility of handle_disconnect bumped to pub(in crate::node) for direct unit-test access. Fixes #60. --- src/node/handlers/dispatch.rs | 14 +++++++++++-- src/node/tests/unit.rs | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/node/handlers/dispatch.rs b/src/node/handlers/dispatch.rs index c441b5e..44707ce 100644 --- a/src/node/handlers/dispatch.rs +++ b/src/node/handlers/dispatch.rs @@ -67,8 +67,12 @@ impl Node { /// Handle a Disconnect notification from a peer. /// /// The peer is signaling an orderly departure. We immediately remove - /// them from all state rather than waiting for timeout detection. - fn handle_disconnect(&mut self, from: &NodeAddr, payload: &[u8]) { + /// them from all state rather than waiting for timeout detection, and + /// schedule a reconnect if the peer is configured as auto-connect. + /// Without this, a graceful upstream shutdown orphans auto-connect + /// entries — other removal paths (link-dead, decrypt failure, peer + /// restart) all schedule reconnect. + pub(in crate::node) fn handle_disconnect(&mut self, from: &NodeAddr, payload: &[u8]) { let disconnect = match crate::protocol::Disconnect::decode(payload) { Ok(msg) => msg, Err(e) => { @@ -83,7 +87,13 @@ impl Node { "Peer sent disconnect notification" ); + let addr = *from; self.remove_active_peer(from); + let now_ms = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_millis() as u64) + .unwrap_or(0); + self.schedule_reconnect(addr, now_ms); } /// Remove an active peer and clean up all associated state. diff --git a/src/node/tests/unit.rs b/src/node/tests/unit.rs index 015762e..97d1ac1 100644 --- a/src/node/tests/unit.rs +++ b/src/node/tests/unit.rs @@ -801,6 +801,43 @@ fn test_schedule_reconnect_fresh_state() { assert_eq!(state.retry_after_ms, 1_000 + expected_delay); } +/// Test that a graceful Disconnect from an auto-connect peer schedules reconnect. +/// +/// Regression test for issue #60: `handle_disconnect` previously called +/// `remove_active_peer` without `schedule_reconnect`, orphaning auto-connect +/// entries on a clean upstream shutdown. Other peer-removal paths (link-dead, +/// decrypt failure, peer restart) all schedule reconnect. +#[test] +fn test_disconnect_schedules_reconnect() { + use crate::protocol::{Disconnect, DisconnectReason}; + + let peer_identity = Identity::generate(); + let peer_npub = peer_identity.npub(); + let peer_node_addr = *PeerIdentity::from_npub(&peer_npub).unwrap().node_addr(); + + let mut config = Config::new(); + config.peers.push(crate::config::PeerConfig::new( + peer_npub, + "udp", + "10.0.0.2:2121", + )); + + let mut node = Node::new(config).unwrap(); + + let payload = Disconnect::new(DisconnectReason::Shutdown).encode(); + node.handle_disconnect(&peer_node_addr, &payload); + + let state = node + .retry_pending + .get(&peer_node_addr) + .expect("handle_disconnect should schedule reconnect for auto-connect peer"); + assert!(state.reconnect, "Entry should be marked as reconnect"); + assert_eq!( + state.retry_count, 0, + "Fresh reconnect after disconnect should start at count=0" + ); +} + /// Test that promote_connection clears retry_pending. #[test] fn test_promote_clears_retry_pending() { From d16acf8ceae8d78656800a61e6aac3527e31ed06 Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Mon, 13 Apr 2026 05:44:23 +0000 Subject: [PATCH 2/5] Reject FIPS mesh addresses in fipsctl connect When a user passes an fd00::/8 address as the endpoint for a udp, tcp, or ethernet transport, the CLI previously echoed success while the daemon silently failed the bind with EAFNOSUPPORT. Mesh ULAs are destinations inside the mesh, not reachable transport endpoints. fipsctl now validates the address up front and prints a clear error with examples, exiting 1 before the control socket call. Other transports (tor) are not inspected since they legitimately accept non-IP endpoints. Covered by inline tests for bare/bracketed/with-port ULA syntaxes, non-ULA IPv6, IPv4, hostnames, and the transport filter. Fixes #61. --- src/bin/fipsctl.rs | 108 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/bin/fipsctl.rs b/src/bin/fipsctl.rs index 9c36e68..12a9333 100644 --- a/src/bin/fipsctl.rs +++ b/src/bin/fipsctl.rs @@ -9,6 +9,7 @@ use fips::upper::hosts::HostMap; use fips::version; use fips::{Identity, encode_nsec}; use std::io::{BufRead, BufReader, Write}; +use std::net::{Ipv6Addr, SocketAddrV6}; use std::os::unix::net::UnixStream; use std::path::{Path, PathBuf}; use std::time::Duration; @@ -199,6 +200,50 @@ fn print_response(value: &serde_json::Value) { println!("{}", output.unwrap_or_else(|_| format!("{value}"))); } +/// Check if `address` is an IPv6 literal in `fd00::/8` (FIPS mesh ULA range). +/// +/// Handles three common syntaxes: +/// - bare IPv6: `fd9d:...` +/// - bracketed + port: `[fd9d:...]:2121` +/// - bare IPv6 + port: `fd9d:...:2121` (ambiguous; accepted if tail is numeric) +fn is_fips_mesh_address(address: &str) -> bool { + let is_ula = |a: &Ipv6Addr| a.octets()[0] == 0xfd; + + if let Ok(a) = address.parse::() { + return is_ula(&a); + } + if let Ok(sa) = address.parse::() { + return is_ula(sa.ip()); + } + if let Some((host, port)) = address.rsplit_once(':') + && port.chars().all(|c| c.is_ascii_digit()) + && !port.is_empty() + { + let host = host.trim_start_matches('[').trim_end_matches(']'); + if let Ok(a) = host.parse::() { + return is_ula(&a); + } + } + false +} + +/// Reject `fd00::/8` addresses for transports that expect a reachable network endpoint. +/// +/// FIPS mesh ULAs are derived from npubs and only make sense as destinations +/// inside an already-established mesh — they are not valid udp/tcp/ethernet +/// transport endpoints. Without this check the CLI echoes success while the +/// daemon rejects the bind with EAFNOSUPPORT (issue #61). +fn validate_connect_address(address: &str, transport: &str) -> Result<(), String> { + let checked = matches!(transport, "udp" | "tcp" | "ethernet"); + if checked && is_fips_mesh_address(address) { + return Err(format!( + "'{address}' is a FIPS mesh address (fd00::/8), not a reachable {transport} endpoint.\n\ + Provide the peer's routable IP/hostname and port (e.g., '192.0.2.1:2121' or 'peer.example.com:2121')." + )); + } + Ok(()) +} + /// Resolve a peer identifier to an npub. /// /// If the identifier starts with "npub1", it's returned as-is. @@ -275,6 +320,10 @@ fn main() { address, transport, } => { + if let Err(e) = validate_connect_address(address, transport) { + eprintln!("error: {e}"); + std::process::exit(1); + } let npub = resolve_peer(peer); build_command( "connect", @@ -300,3 +349,62 @@ fn main() { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn detects_bare_ula_literal() { + assert!(is_fips_mesh_address("fd9d:abcd::1")); + assert!(is_fips_mesh_address("fd00::")); + assert!(is_fips_mesh_address( + "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + )); + } + + #[test] + fn detects_bracketed_ula_with_port() { + assert!(is_fips_mesh_address("[fd9d:abcd::1]:2121")); + assert!(is_fips_mesh_address("[fd00::1]:8443")); + } + + #[test] + fn detects_bare_ula_with_port() { + assert!(is_fips_mesh_address("fd9d:abcd::1:2121")); + } + + #[test] + fn rejects_non_ula_ipv6() { + // fc00::/7 other half (fcXX:) is also ULA but not fd00::/8 — we only + // block the fd-prefixed half that FIPS actually uses. + assert!(!is_fips_mesh_address("fc00::1")); + assert!(!is_fips_mesh_address("::1")); + assert!(!is_fips_mesh_address("2001:db8::1")); + assert!(!is_fips_mesh_address("[2001:db8::1]:2121")); + } + + #[test] + fn ignores_ipv4_and_hostnames() { + assert!(!is_fips_mesh_address("192.0.2.1:2121")); + assert!(!is_fips_mesh_address("peer.example.com:2121")); + assert!(!is_fips_mesh_address("coinos.pro:2121")); + } + + #[test] + fn validates_only_target_transports() { + assert!(validate_connect_address("fd9d::1:2121", "udp").is_err()); + assert!(validate_connect_address("fd9d::1:2121", "tcp").is_err()); + assert!(validate_connect_address("fd9d::1:2121", "ethernet").is_err()); + // Other transports are not inspected — they may legitimately accept + // non-IP endpoints (tor onion, etc.). + assert!(validate_connect_address("fd9d::1:2121", "tor").is_ok()); + } + + #[test] + fn allows_valid_endpoints() { + assert!(validate_connect_address("192.0.2.1:2121", "udp").is_ok()); + assert!(validate_connect_address("peer.example.com:2121", "tcp").is_ok()); + assert!(validate_connect_address("[2001:db8::1]:2121", "udp").is_ok()); + } +} From 7e002a3883d13492ca676ae50f7cf3df24733ac0 Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Mon, 13 Apr 2026 05:49:37 +0000 Subject: [PATCH 3/5] Update CHANGELOG for pending 0.2.1 bug fixes Captures three fixes landed on maint since 0.2.0 that were not yet recorded: the bloom-filter greedy-tree fallback fix, auto-connect reconnect on graceful disconnect (#60), and fipsctl mesh-address rejection (#61). --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f603332..4873bd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 unavailable on OpenWrt targets - IPv6 routing policy rule added at TUN setup to protect `fd00::/8` from interception by Tailscale's table 52 default route +- Bloom filter routing no longer swallows traffic when no bloom + candidate is strictly closer than the current node. `find_next_hop` + now falls through to greedy tree routing in that case instead of + returning `NoRoute`, which previously caused dropped packets in + topologies where the tree parent was closer but not a bloom + candidate +- Auto-connect peers now reconnect after a graceful `Disconnect` + notification from the remote side. `handle_disconnect` previously + removed the peer without scheduling a reconnect, orphaning the + entry on a clean upstream shutdown; the other removal paths + (link-dead, decrypt failure, peer restart) already scheduled + reconnect ([#60](https://github.com/jmcorgan/fips/issues/60), + reported by [@SwapMarket](https://github.com/SwapMarket)) +- `fipsctl connect` now rejects FIPS mesh (`fd00::/8`) addresses for + `udp`, `tcp`, and `ethernet` transports with a clear error message + instead of echoing success while the daemon silently failed the + bind with `EAFNOSUPPORT` + ([#61](https://github.com/jmcorgan/fips/issues/61), + reported by [@SwapMarket](https://github.com/SwapMarket)) ## [0.2.0] - 2026-03-22 From 6698c4d6697cb6d5cdfd1ebe397e65a998bd4337 Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Mon, 13 Apr 2026 06:20:45 +0000 Subject: [PATCH 4/5] Expand CHANGELOG Unreleased for master-only changes Adds entries for master-only work since 0.2.0 that wasn't captured yet: Windows and macOS platform support, the outbound LAN gateway and its packaging, the macOS WireGuard sidecar example, multi-backend .fips DNS configuration, the node.log_level config, the Nostr UDP hole punch protocol proposal doc, the MMP report interval retune, the info-to-debug log demotion, the rekey msg1 gate fix, and the fipstop ratatui try_init change. Fixed entries only cover bugs present in 0.2.0. Fixes against master-only code (BLE reliability work, new sidecar port mapping) are rolled into their respective Added entries rather than listed as Fixed, per Keep a Changelog conventions. --- CHANGELOG.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7426f6..e3f422d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +#### Platform Support + +- Windows platform support: wintun TUN device, TCP control socket on + `localhost:21210` (in place of the Unix domain socket), Windows + Service lifecycle (`--install-service`, `--uninstall-service`, + `--service`), ZIP packaging with PowerShell install/uninstall scripts, + and CI build/test matrix entry + ([#45](https://github.com/jmcorgan/fips/pull/45)) +- macOS platform support: native `utun` TUN interface management, raw + Ethernet transport via BPF, `.pkg` packaging with launchd plist and + uninstall script, x86_64 cross-compile from arm64, and CI build/unit + test jobs +- `gateway` Cargo feature flag gates the optional Linux-only + `rustables` dependency so macOS and Windows builds never pull in + nftables bindings + +#### Outbound LAN Gateway + +- New `fips-gateway` binary that lets unmodified LAN hosts reach FIPS + mesh destinations via DNS-allocated virtual IPs and kernel nftables + NAT. Virtual-IP pool (`fd01::/112` by default) with state-machine + lifecycle and TTL-based reclamation; conntrack-backed session + tracking; proxy NDP on the LAN interface; control socket at + `/run/fips/gateway.sock` with `show_gateway` and `show_mappings`; + fipstop Gateway tab with pool gauge and mappings table; design doc + at `docs/design/fips-gateway.md`; integration test harness +- Gateway packaging: systemd service unit with `After=fips.service`, + Debian and AUR package entries, OpenWrt procd init with dnsmasq + forwarding, proxy NDP, RA route advertisements, and IPv6 forwarding + sysctls. Gateway enabled by default on OpenWrt + +#### Examples + +- macOS WireGuard sidecar: run FIPS in a local Docker container and + route `.fips` traffic from the macOS host through a WireGuard tunnel + to the container's `fips0` interface. Only traffic destined for + `fd00::/8` transits the sidecar; regular internet traffic continues + to use the host network + ([#51](https://github.com/jmcorgan/fips/pull/51)) + #### Bluetooth Transport - Bluetooth Low Energy (BLE) L2CAP Connection-Oriented Channel transport @@ -20,6 +60,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Cross-probe tie-breaker using deterministic NodeAddr comparison - Connection pool with configurable capacity and eviction +#### DNS + +- Multi-backend `.fips` DNS configuration: a detection script + configures whichever resolver is available, in priority order: + systemd dns-delegate (systemd >= 258), systemd-resolved via + `resolvectl`, standalone dnsmasq, NetworkManager with the dnsmasq + plugin. Teardown reads the recorded backend from + `/run/fips/dns-backend` and reverses only what was applied + ([#58](https://github.com/jmcorgan/fips/pull/58), + fixes [#52](https://github.com/jmcorgan/fips/issues/52)) + +#### Operator Configuration + +- `node.log_level` config field (case-insensitive, default `info`) + replaces the hardcoded `RUST_LOG=info` previously baked into + systemd units and the OpenWrt procd init script. The daemon now + loads config before initializing tracing so the configured level + takes effect; `RUST_LOG` still overrides when set + +#### Documentation + +- Pre-implementation proposal for NAT traversal using Nostr relays + as the signaling channel and STUN for reflexive address discovery + (`docs/proposals/`) + #### Packaging and Deployment - Linux release artifact workflow: builds x86_64 and aarch64 tarballs @@ -30,6 +95,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#21](https://github.com/jmcorgan/fips/pull/21), [@dskvr](https://github.com/dskvr)) +### Changed + +- MMP link-layer report intervals retuned for constrained transports: + steady-state floor raised from 100ms to 1000ms, ceiling from 2000ms + to 5000ms. Cold-start uses a 200ms floor for the first 5 SRTT samples + before switching to steady-state. Reduces BLE overhead ~10× while + keeping reports well above the EWMA convergence threshold. + Session-layer intervals unchanged +- 35 info-level log messages demoted to debug (handshake + cross-connection mechanics, periodic MMP telemetry, TUN/transport + shutdown, retry scheduling). Info output now focuses on + operator-relevant state changes: lifecycle events, peer promotions, + session establishment, parent switches, transport start/stop + ### Fixed - Control socket path detection in fipsctl and fipstop now checks for @@ -61,6 +140,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 bind with `EAFNOSUPPORT` ([#61](https://github.com/jmcorgan/fips/issues/61), reported by [@SwapMarket](https://github.com/SwapMarket)) +- Rekey msg1 on non-accepting transports (e.g. UDP holepunch) was + rejected at the top of `handle_msg1()`, which broke rekey handshakes + on established links and produced repeated "dual rekey initiation" + log floods. The gate now only blocks truly new inbound handshakes + from unknown addresses; rekey and restart msg1s for established + peers are processed normally + ([#47](https://github.com/jmcorgan/fips/issues/47), + [#49](https://github.com/jmcorgan/fips/pull/49)) +- `fipstop` now uses `ratatui::try_init()` instead of `ratatui::init()`, + so terminal initialization failures (e.g. Docker on macOS Sequoia, + or environments without a usable tty) produce a clean error message + instead of a hard crash ## [0.2.0] - 2026-03-22 From 5029b40d4999ed559a4ea85634ab519e530e0171 Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Mon, 13 Apr 2026 06:33:01 +0000 Subject: [PATCH 5/5] Update README for current platform, transport, and example state - Correct the transport matrix: UDP, TCP, and Tor work on Windows (previously shown as unsupported). Add an OpenWrt column with BLE disabled due to missing libdbus on the target. - Mention the `.fips` DNS resolver and outbound LAN gateway in the Features list, and add the gateway bullet under What works today. - Reframe the Linux DNS resolver setup: the `.deb` package now auto-configures the available backend; the manual resolvectl snippet is shown for tarball and manual installs. - Expand the Examples section to list all three example deployments (Nostr relay sidecar, K8s sidecar, macOS WireGuard sidecar) rather than only the macOS one. - Refresh Project Structure to include the `fips-gateway` binary, the full packaging list (macOS .pkg, Windows ZIP, OpenWrt ipk, AUR in addition to Debian and systemd tarball), and the examples directory. - Mention macOS `.pkg` and Windows ZIP/service packaging in the packaging line of What works today. --- README.md | 64 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index ceccad3..8cf6cc8 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,12 @@ endpoints. end-to-end session encryption (XK), with periodic rekey for forward secrecy - **Nostr-native identity** — secp256k1 keypairs as node addresses, no registration or central authority -- **IPv6 adaptation** — TUN interface maps npubs to fd00::/8 addresses for - unmodified IP applications; static hostname mapping (`/etc/fips/hosts`) +- **IPv6 adaptation** — TUN interface maps npubs to fd00::/8 addresses + for unmodified IP applications; built-in `.fips` DNS resolver with + optional static hostname mapping (`/etc/fips/hosts`) +- **Outbound LAN gateway** — optional `fips-gateway` daemon lets + unmodified LAN hosts reach `.fips` destinations via a + DNS-allocated virtual IP pool and kernel nftables NAT - **Metrics Measurement Protocol** — per-link RTT, loss, jitter, and goodput measurement with mesh size estimation - **ECN congestion signaling** — hop-by-hop CE flag relay with RFC 3168 IPv6 @@ -68,18 +72,21 @@ supported (see transport matrix below). ### Transport support by platform -| Transport | Linux | macOS | Windows | -|-----------|:-----:|:-----:|:-------:| -| UDP | ✅ | ✅ | ❌ | -| TCP | ✅ | ✅ | ❌ | -| Ethernet | ✅ | ✅ | ❌ | -| Tor | ✅ | ✅ | ❌ | -| BLE | ✅ | ❌ | ❌ | +| Transport | Linux | macOS | Windows | OpenWrt | +|-----------|:-----:|:-----:|:-------:|:-------:| +| UDP | ✅ | ✅ | ✅ | ✅ | +| TCP | ✅ | ✅ | ✅ | ✅ | +| Ethernet | ✅ | ✅ | ❌ | ✅ | +| Tor | ✅ | ✅ | ✅ | ✅ | +| BLE | ✅ | ❌ | ❌ | ❌ | On **Linux**, the BLE transport requires BlueZ and libdbus. On Debian/Ubuntu: `sudo apt install bluez libdbus-1-dev`. Then build with BLE enabled: `cargo build --release --features ble`. +On **OpenWrt**, BLE is disabled because libdbus is not available on +the target. All other transports work and ship in the default ipk. + ## Installation After building, choose one of the following methods to install. @@ -261,7 +268,11 @@ for the full reference. FIPS includes a DNS resolver (enabled by default, port 5354) that maps `.fips` names to fd00::/8 IPv6 addresses. -**Linux** (systemd-resolved): +**Linux**: The `.deb` package auto-detects and configures whichever +resolver is present (systemd dns-delegate, systemd-resolved, dnsmasq, +or NetworkManager with dnsmasq); no manual setup is needed. For +manual or tarball installs, point your resolver at `127.0.0.1:5354` +for the `fips` domain — e.g., with systemd-resolved: ```bash sudo resolvectl dns fips0 127.0.0.1:5354 @@ -322,13 +333,20 @@ including static topology tests and stochastic chaos simulation. ## Examples -- [examples/wireguard-sidecar-macos/](examples/wireguard-sidecar-macos/) - - Run a local WireGuard sidecar on macOS so `.fips` traffic can reach the mesh - through Docker. - -The macOS WireGuard sidecar only forwards FIPS IPv6 traffic destined for -`fd00::/8` from `wg0` to `fips0`. Regular internet traffic does not transit the -sidecar and continues to use the host network normally. +- [examples/sidecar-nostr-relay/](examples/sidecar-nostr-relay/) — + Run a [strfry](https://github.com/hoytech/strfry) Nostr relay + reachable exclusively over the FIPS mesh. The relay container shares + the FIPS sidecar's network namespace and is isolated from the host + network. +- [examples/k8s-sidecar/](examples/k8s-sidecar/) — Run FIPS as a + Kubernetes Pod sidecar. The sidecar creates `fips0` in the Pod's + shared network namespace so every other container in the Pod gets + mesh access without modification. +- [examples/wireguard-sidecar-macos/](examples/wireguard-sidecar-macos/) — + Reach the FIPS mesh from a macOS host through a local Docker + container over a WireGuard tunnel. Only traffic destined for + `fd00::/8` transits the sidecar; regular internet traffic continues + to use the host network. ## Documentation @@ -345,8 +363,9 @@ If you want to contribute, start with: ## Project Structure ```text -src/ Rust source (library + fips/fipsctl/fipstop binaries) -packaging/ Debian, systemd tarball, and shared packaging files +src/ Rust source (library + fips/fipsctl/fipstop/fips-gateway binaries) +packaging/ Debian, macOS .pkg, Windows ZIP, OpenWrt ipk, AUR, systemd tarball +examples/ Deployment examples (Nostr relay, K8s sidecar, macOS WireGuard) docs/design/ Protocol design specifications testing/ Docker-based integration test harnesses ``` @@ -363,14 +382,17 @@ Ethernet, Tor, and Bluetooth (BLE) with a small live mesh of deployed nodes. - Noise IK (link layer) and Noise XK (session layer) encryption - Periodic Noise rekey with hitless cutover for forward secrecy (FMP + FSP) - Persistent node identity with key file management -- IPv6 TUN adapter with DNS resolution of `.fips` names +- IPv6 TUN adapter with built-in `.fips` DNS resolver and multi-backend + auto-configuration (systemd dns-delegate, systemd-resolved, dnsmasq, + NetworkManager) - Static hostname mapping (`/etc/fips/hosts`) with auto-reload - Per-link metrics (RTT, loss, jitter, goodput) and mesh size estimation - ECN congestion signaling (hop-by-hop CE relay, IPv6 CE marking, kernel drop detection) - UDP, TCP, Ethernet, Tor, and BLE transports (BLE via L2CAP CoC with per-link MTU negotiation) +- Outbound LAN gateway for unmodified hosts via DNS-allocated virtual IPs and nftables NAT - Runtime inspection and peer management via `fipsctl` and `fipstop` - Reproducible builds with toolchain pinning and SOURCE_DATE_EPOCH -- Linux (Debian, systemd tarball, OpenWrt, AUR) and macOS packaging +- Linux (Debian, systemd tarball, OpenWrt, AUR), macOS (`.pkg`), and Windows (ZIP, service) packaging - Docker-based integration and chaos testing ### Near-term priorities