diff --git a/README.md b/README.md index b751761..5b12879 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # FIPS: Free Internetworking Peering System +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![Rust](https://img.shields.io/badge/rust-1.85%2B-orange.svg)](https://www.rust-lang.org/) +[![Status](https://img.shields.io/badge/status-alpha%20(0.1.0)-yellow.svg)](#status--roadmap) + A distributed, decentralized network routing protocol for mesh nodes connecting over arbitrary transports. -> **Status: Experimental / Pre-release** +> **Status: Alpha (0.1.0)** > > FIPS is under active development. The protocol and APIs are not stable. -> Expect breaking changes. +> Expect breaking changes. See [Status & Roadmap](#status--roadmap) below. ## Overview @@ -38,8 +42,8 @@ sessions across the mesh. measurement - **Operator visibility** — `fipsctl` control socket interface for runtime inspection of peers, links, sessions, tree state, and metrics -- **Zero configuration** — sensible defaults; a node can run with no config - file +- **Zero configuration** — sensible defaults; a node can start with no config + file, though peer addresses are needed to join a network ## Quick Start @@ -59,16 +63,77 @@ cargo build --release ### Run ``` -# With default configuration (ephemeral identity, default ports): +# Start with default search paths (see below): sudo ./target/release/fips -# With a configuration file: +# With an explicit configuration file: sudo ./target/release/fips -c fips.yaml ``` +Without `-c`, the node searches for `fips.yaml` in these locations +(highest priority first, values from later files override earlier ones): + +1. `./fips.yaml` (current directory) +2. `~/.config/fips/fips.yaml` (user config) +3. `/etc/fips/fips.yaml` (system) + +If no config file is found, the node starts with defaults (ephemeral +identity, default ports, no peers). + +A minimal two-node setup (each node points at the other): + +```yaml +# node-a.yaml # node-b.yaml +node: # node: + identity: # identity: + nsec: "nsec1aaa..." # nsec: "nsec1bbb..." +transports: # transports: + udp: # udp: + bind_addr: "0.0.0.0:4000" # bind_addr: "0.0.0.0:4000" +peers: # peers: + - npub: "npub1bbb..." # - npub: "npub1aaa..." + addresses: # addresses: + - transport: udp # - transport: udp + addr: "10.0.0.2:4000" # addr: "10.0.0.1:4000" +``` + +The `nsec` field accepts bech32 (`nsec1...`) or hex-encoded secret keys. +Omit it entirely for an ephemeral identity that changes each restart. + See [docs/design/fips-configuration.md](docs/design/fips-configuration.md) for the full configuration reference. +### Test Connectivity + +FIPS includes a built-in DNS resolver (enabled by default, port 5354) +that maps `.fips` names to fd00::/8 IPv6 addresses derived from each +node's public key. Configure your system to send `.fips` queries to it. + +With systemd-resolved: + +``` +sudo resolvectl dns fips0 127.0.0.1:5354 +sudo resolvectl domain fips0 ~fips +``` + +Or manually in `/etc/resolv.conf` (routes all DNS through FIPS for +`.fips` names only if your resolver supports conditional forwarding; +otherwise this sets it as a general nameserver): + +``` +nameserver 127.0.0.1 +options port:5354 +``` + +Once DNS is configured, ping a peer by npub: + +``` +ping6 npub1bbb....fips +``` + +Any IPv6-capable application can reach FIPS nodes this way — `ping6`, +`ssh`, `curl`, etc. + ### Inspect While a node is running, use `fipsctl` to inspect its state: @@ -108,9 +173,37 @@ a layered protocol specification. Start with src/ Rust source (library + fips/fipsctl binaries) docs/design/ Protocol design specifications testing/ Docker-based integration test harnesses -benches/ Criterion benchmarks ``` +## Status & Roadmap + +FIPS is at **v0.1.0 (alpha)**. The core protocol works end-to-end over +UDP/IP overlays but has not been tested beyond small meshes. + +### What works today + +- Spanning tree construction with greedy coordinate routing +- Bloom filter discovery for finding nodes without global state +- Noise IK (link layer) and Noise XK (session layer) encryption +- IPv6 TUN adapter with DNS resolution of `.fips` names +- Per-link metrics (RTT, loss, jitter, goodput) +- Runtime inspection via `fipsctl` +- Docker-based integration and chaos testing + +### Near-term priorities + +- Peer discovery via Nostr relays (bootstrap without static peer lists) +- Additional transports (Ethernet, Tor) +- Improved routing resilience under churn +- Security audit of cryptographic protocols +- CI pipeline and published crate + +### Longer-term + +- Mobile platform support +- Bandwidth-aware routing and QoS +- Protocol stability and versioned wire format + ## License MIT — see [LICENSE](LICENSE). diff --git a/docs/design/README.md b/docs/design/README.md index c4a2796..e171f1d 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -48,27 +48,4 @@ specific topics. ## Document Relationships -```text - fips-intro.md - │ - ┌──────────────┼──────────────┐ - ▼ ▼ │ - fips-transport-layer fips-mesh- │ - │ operation │ - ▼ │ │ - fips-mesh-layer ◄────────┤ │ - │ │ │ - ▼ ├──► fips-spanning-tree - fips-session-layer │ │ - │ └──► fips-bloom-filters - ▼ - fips-ipv6-adapter - fips-wire-formats - (referenced by all layer docs) - - fips-configuration - (standalone reference) - - spanning-tree-dynamics - (pedagogical companion to fips-spanning-tree) -``` +![Document relationships](document-relationships.svg) diff --git a/docs/design/document-relationships.svg b/docs/design/document-relationships.svg new file mode 100644 index 0000000..dcc0860 --- /dev/null +++ b/docs/design/document-relationships.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + Document Relationships + + + + fips-intro.md + + + + + + + + fips-transport-layer.md + + + + fips-mesh-operation.md + + + + + + + fips-mesh-layer.md + + + + + + + + + + fips-session-layer.md + + + + + + + fips-ipv6-adapter.md + + + + + + + + + + fips-spanning-tree.md + + + + + + + fips-bloom-filters.md + + + + + + + fips-wire-formats.md + (referenced by all layer docs) + + + + fips-configuration.md + (standalone reference) + + + + spanning-tree-dynamics.md + (companion to fips-spanning-tree) + + + + Protocol stack + + Mesh behavior + + Supporting references + + Wire formats + + Implementation + diff --git a/docs/design/fips-configuration.md b/docs/design/fips-configuration.md index 02e6f3a..4d3f00a 100644 --- a/docs/design/fips-configuration.md +++ b/docs/design/fips-configuration.md @@ -229,7 +229,7 @@ with the node for routing. | Parameter | Type | Default | Description | |-----------|------|---------|-------------| -| `dns.enabled` | bool | `false` | Enable DNS responder | +| `dns.enabled` | bool | `true` | Enable DNS responder | | `dns.bind_addr` | string | `"127.0.0.1"` | Bind address | | `dns.port` | u16 | `5354` | Listen port | | `dns.ttl` | u32 | `300` | AAAA record TTL in seconds | @@ -387,7 +387,7 @@ tun: mtu: 1280 dns: - enabled: false + enabled: true bind_addr: "127.0.0.1" port: 5354 ttl: 300 diff --git a/src/config/mod.rs b/src/config/mod.rs index bbe37e5..36ffd9e 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -186,10 +186,8 @@ impl Config { if other.tun.mtu.is_some() { self.tun.mtu = other.tun.mtu; } - // Merge dns section - if other.dns.enabled { - self.dns.enabled = true; - } + // Merge dns section — higher-priority config always wins for enabled + self.dns.enabled = other.dns.enabled; if other.dns.bind_addr.is_some() { self.dns.bind_addr = other.dns.bind_addr; } diff --git a/src/upper/config.rs b/src/upper/config.rs index 3286b61..c1e77aa 100644 --- a/src/upper/config.rs +++ b/src/upper/config.rs @@ -20,11 +20,15 @@ const DEFAULT_DNS_PORT: u16 = 5354; /// Default DNS record TTL in seconds (5 minutes). const DEFAULT_DNS_TTL: u32 = 300; +fn default_true() -> bool { + true +} + /// DNS responder configuration (`dns.*`). -#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct DnsConfig { - /// Enable DNS responder (`dns.enabled`). - #[serde(default, skip_serializing_if = "std::ops::Not::not")] + /// Enable DNS responder (`dns.enabled`, default: true). + #[serde(default = "default_true")] pub enabled: bool, /// Bind address (`dns.bind_addr`). @@ -40,6 +44,17 @@ pub struct DnsConfig { pub ttl: Option, } +impl Default for DnsConfig { + fn default() -> Self { + Self { + enabled: true, + bind_addr: None, + port: None, + ttl: None, + } + } +} + impl DnsConfig { /// Get the bind address (default: 127.0.0.1). pub fn bind_addr(&self) -> &str {