mirror of
https://github.com/jmcorgan/fips.git
synced 2026-10-05 19:18:25 +00:00
Bring the changelog current and correct four documentation defects
The Unreleased block is rebuilt from a walk of all 117 commits since v0.4.1 rather than from what the block already held, which is how six gaps surfaced. Two of them were whole missing effects. The identity write path discarded six results, so a node configured for a persistent identity could fall through to an ephemeral one in silence and change its npub, routing address and mesh address on every start. And the OpenWrt zig download verification was described only in part. Chronological fix sequences are collapsed to their net state, and fixes for bugs introduced and closed inside this cycle are folded away rather than described, since no user ever saw them. Sixty-one CI and harness commits are summarized rather than left out, because they change what a contributor running the local pipeline sees. The flat lists are reorganized into subsections by area. Everything from 0.4.1 down is untouched. Two entries carry effects no commit message mentioned. Clearing every copy of private key material added Drop to four public types, so their fields can no longer be moved out, which is source-breaking for anyone using the crate as a library and is reachable through node.identity on the public config. And the responder-side rekey narrowing covers five call sites, not the four the original entry claimed; the ack initiator arm is the one that deliberately still abandons the whole rekey. The four test-harness fixes landed since then get entries under the CI and test-harness heading, written as what a contributor sees: a failing harness that names the condition instead of exiting bare, dns-resolver scenarios that stop burning the full boot timeout on a container that booted correctly, and a chaos harness that checks its teardown and node stops actually happened rather than assuming it. Four documentation defects are fixed alongside. Two sent macOS readers to paths that do not exist there: the configuration reference stated the highest-priority system config path as /etc/fips/fips.yaml unconditionally, where the macOS package installs under /usr/local/etc/fips/, and the persistent-identity tutorial had the same problem throughout with nothing saying its paths were Linux ones. It now opens with the substitution table, notes that the daemon derives the key directory from whichever config it loaded, and points at the migration recipe for a host already carrying keys at the old path. The other two described test coverage that does not exist. The testing readme claimed twenty chaos scenarios where ten exist, and the chaos readme documented three that are in neither runner nor tree. Both scenario tables are rewritten from the files, and bloom-storm is described honestly as retired from both runners with no replacement, which is a coverage gap rather than a migration to other tests. The readme's Rust badge asserted 1.85+ while rust-toolchain.toml pins something else, so the badge no longer carries a version and the toolchain file is the only place that states one.
This commit is contained in:
+609
-425
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
|
||||

|
||||
[](LICENSE)
|
||||
[](https://www.rust-lang.org/)
|
||||
[](https://www.rust-lang.org/)
|
||||
[](#status--roadmap)
|
||||
|
||||
A self-organizing encrypted mesh network built on Nostr identities,
|
||||
|
||||
@@ -20,8 +20,10 @@ locations, lowest to highest priority:
|
||||
|
||||
All found files are loaded and merged in priority order. Values from higher
|
||||
priority files override those from lower priority files. This allows a system
|
||||
administrator to set site-wide defaults in `/etc/fips/fips.yaml` while
|
||||
individual deployments override specific values in `./fips.yaml`.
|
||||
administrator to set site-wide defaults in the priority 1 path above,
|
||||
`/usr/local/etc/fips/fips.yaml` on macOS and `/etc/fips/fips.yaml` on other
|
||||
Unix systems, while individual deployments override specific values in
|
||||
`./fips.yaml`.
|
||||
|
||||
### CLI Option
|
||||
|
||||
|
||||
@@ -28,6 +28,11 @@ The whole exercise should take about ten minutes.
|
||||
your stable nsec / npub
|
||||
```
|
||||
|
||||
The diagram shows the Linux layout. On macOS the same three files
|
||||
live under `/usr/local/etc/fips/`; read
|
||||
[Where these files live](#where-these-files-live) before running any
|
||||
command below.
|
||||
|
||||
After this tutorial your node will have:
|
||||
|
||||
- A keypair on disk that the daemon reuses across restarts.
|
||||
@@ -36,6 +41,28 @@ After this tutorial your node will have:
|
||||
- A clear understanding of which file holds the secret and how
|
||||
to keep it that way.
|
||||
|
||||
## Where these files live
|
||||
|
||||
Every path in this tutorial is written in its Linux form. The macOS
|
||||
package (`.pkg`) installs config and keys under
|
||||
`/usr/local/etc/fips/` instead of `/etc/fips/`, so on macOS
|
||||
substitute as you go:
|
||||
|
||||
| Linux / other Unix | macOS |
|
||||
| --- | --- |
|
||||
| `/etc/fips/fips.yaml` | `/usr/local/etc/fips/fips.yaml` |
|
||||
| `/etc/fips/fips.key` | `/usr/local/etc/fips/fips.key` |
|
||||
| `/etc/fips/fips.pub` | `/usr/local/etc/fips/fips.pub` |
|
||||
|
||||
`fipsctl keygen` writes to `/usr/local/etc/fips/` by default on
|
||||
macOS. The daemon still probes `/etc/fips/fips.yaml` as a fallback,
|
||||
so an existing install is not broken by an upgrade, but the macOS
|
||||
packaging only installs files under `/usr/local/etc/fips/`. If a
|
||||
macOS host already carries key files at the old `/etc/fips/` path,
|
||||
the daemon uses the old key and warns rather than minting a new
|
||||
identity; the migration recipe is in the
|
||||
[how-to guide](../how-to/persistent-identity.md).
|
||||
|
||||
## Why a stable identity matters
|
||||
|
||||
In FIPS your Nostr keypair *is* your node's identity in the most
|
||||
@@ -68,7 +95,8 @@ The daemon supports two ways of holding that keypair:
|
||||
> identity unless you explicitly ask for one.
|
||||
> - *Persistent*: the daemon reads (or, on first start,
|
||||
> generates and writes) a keypair stored at
|
||||
> `/etc/fips/fips.key`. The npub stays the same across
|
||||
> `/etc/fips/fips.key` (`/usr/local/etc/fips/fips.key` on
|
||||
> macOS). The npub stays the same across
|
||||
> restarts, reboots, and reinstalls as long as that file is
|
||||
> preserved. You take on the cost of protecting an on-disk
|
||||
> secret in exchange for being addressable by a stable name.
|
||||
@@ -117,9 +145,9 @@ Make a note of it. We expect this to change.
|
||||
|
||||
## Step 2: Enable persistent identity in the config
|
||||
|
||||
Open `/etc/fips/fips.yaml` and find the `node:` block. The
|
||||
shipped default has the relevant fragment commented out; make it
|
||||
look like this:
|
||||
Open `/etc/fips/fips.yaml` (`/usr/local/etc/fips/fips.yaml` on
|
||||
macOS) and find the `node:` block. The shipped default has the
|
||||
relevant fragment commented out; make it look like this:
|
||||
|
||||
```yaml
|
||||
node:
|
||||
@@ -137,6 +165,9 @@ The daemon's behavior on the next restart:
|
||||
`/etc/fips/fips.{key,pub}` with the correct file modes, and
|
||||
use that.
|
||||
|
||||
The daemon derives the key directory from whichever config file it
|
||||
loaded, so on macOS both files land in `/usr/local/etc/fips/`.
|
||||
|
||||
## Step 3: Restart the daemon
|
||||
|
||||
```sh
|
||||
@@ -164,6 +195,9 @@ The daemon wrote two files:
|
||||
sudo ls -l /etc/fips/fips.key /etc/fips/fips.pub
|
||||
```
|
||||
|
||||
On macOS, list `/usr/local/etc/fips/fips.key` and
|
||||
`/usr/local/etc/fips/fips.pub` instead.
|
||||
|
||||
Expect:
|
||||
|
||||
```text
|
||||
@@ -239,7 +273,7 @@ old one will be stale.
|
||||
persistent identity.
|
||||
- **Where it lives.** `/etc/fips/fips.key` and
|
||||
`/etc/fips/fips.pub`, mode `0600` and `0644`, owned
|
||||
`root:root`.
|
||||
`root:root`; under `/usr/local/etc/fips/` on macOS.
|
||||
- **What to share.** `fips.pub` is public; `fips.key` is not.
|
||||
- **What it buys you.** A npub other operators can add to their
|
||||
`peers:` list once, and that addresses the services your node
|
||||
@@ -250,11 +284,13 @@ old one will be stale.
|
||||
If the post-restart npub does not match `fips.pub`:
|
||||
|
||||
- **Check file permissions.**
|
||||
`sudo ls -l /etc/fips/fips.key`. If the mode is not `0600` or
|
||||
the owner is not `root:root`, the daemon may have refused to
|
||||
read it. Restore with
|
||||
`sudo ls -l /etc/fips/fips.key`, or
|
||||
`sudo ls -l /usr/local/etc/fips/fips.key` on macOS. If the mode
|
||||
is not `0600` or the owner is not `root:root`, the daemon may
|
||||
have refused to read it. Restore with
|
||||
`sudo chmod 0600 /etc/fips/fips.key && sudo chown root:root
|
||||
/etc/fips/fips.key`.
|
||||
/etc/fips/fips.key`, substituting the macOS path where it
|
||||
applies.
|
||||
- **Check the journal.** `sudo journalctl -u fips -n 100` after
|
||||
the restart will show one of:
|
||||
- `Loaded persistent identity from key file path=...` — good.
|
||||
|
||||
+2
-2
@@ -45,8 +45,8 @@ and a local STUN responder.
|
||||
Automated network testing with configurable node counts, topology
|
||||
algorithms (random geometric, Erdos-Renyi, chain, explicit), and fault
|
||||
injection (netem mutation, link flaps, traffic generation, node
|
||||
churn). 20 scenarios covering general stress testing, cost-based parent
|
||||
selection, mixed link technologies (fiber/Bluetooth/WiFi),
|
||||
churn). 10 scenarios covering general stress and node churn, discovery
|
||||
over sparse topologies, spanning-tree and bloom-propagation regression,
|
||||
transport-specific validation (UDP, TCP, Ethernet), and ECN/congestion
|
||||
testing. Scenarios are
|
||||
defined in YAML and executed via a Python harness that manages the full
|
||||
|
||||
+85
-36
@@ -3,10 +3,11 @@
|
||||
Automated network testing for FIPS. Generates random or explicit
|
||||
topologies, spins up Docker containers, and applies configurable
|
||||
stressors (network impairment, link flaps, traffic generation, node
|
||||
churn) over a timed simulation run. Scenarios cover general stress
|
||||
testing, cost-based parent selection, mixed link technologies
|
||||
(fiber/Bluetooth/WiFi), and transport-specific validation (UDP, TCP,
|
||||
Ethernet). Logs are collected and analyzed automatically.
|
||||
churn) over a timed simulation run. Scenarios cover general stress and
|
||||
node churn, discovery over sparse topologies, spanning-tree and
|
||||
bloom-propagation regression, transport-specific validation (UDP, TCP,
|
||||
Ethernet), and ECN/congestion testing. Logs are collected and analyzed
|
||||
automatically.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -23,24 +24,56 @@ Ethernet). Logs are collected and analyzed automatically.
|
||||
|
||||
## Available Scenarios
|
||||
|
||||
### General stress tests
|
||||
### General stress and churn
|
||||
|
||||
Random topologies with increasing stressor intensity.
|
||||
Random topologies with increasing stressor intensity. All three enable
|
||||
netem mutation, link flaps, iperf traffic, node churn and bandwidth
|
||||
tiers, and differ in transport mix, density, and whether the peer set
|
||||
itself churns. Each takes a `--nodes N` override, so the node counts
|
||||
below are defaults rather than fixed sizes.
|
||||
|
||||
| Scenario | Nodes | Topology | Duration | Netem | Link Flaps | Traffic | Node Churn | Bandwidth |
|
||||
| -------- | ----- | ---------------- | -------- | ----- | ---------- | ------- | ---------- | --------- |
|
||||
| chaos-10 | 10 | random_geometric | 120s | yes | yes | yes | -- | -- |
|
||||
| churn-10 | 10 | random_geometric | 600s | yes | yes | yes | yes | -- |
|
||||
| churn-20 | 20 | erdos_renyi | 600s | yes | yes | yes | yes | yes |
|
||||
| Scenario | Nodes | Topology | Duration | Peer churn |
|
||||
| ---------------- | ----- | ---------------- | -------- | ---------- |
|
||||
| churn-mixed | 20 | erdos_renyi | 600s | -- |
|
||||
| maelstrom | 20 | erdos_renyi | 600s | yes |
|
||||
| maelstrom-sparse | 50 | random_geometric | 600s | yes |
|
||||
|
||||
- **chaos-10**: Network degradation (5-50ms delay, 0-2% loss), link flaps (max 2
|
||||
down, 10-30s), and iperf traffic (max 3 concurrent). Netem mutates 30% of
|
||||
links every 15-30s between normal and degraded policies.
|
||||
- **churn-10**: Extended run with node churn (1 node down at a time, 30-90s).
|
||||
Tests tree re-convergence after node departure/rejoin.
|
||||
- **churn-20**: Aggressive scale test. Erdos-Renyi topology, up to 5 nodes down
|
||||
simultaneously, bandwidth tiers (1/10/100/1000 Mbps), `protect_connectivity`
|
||||
disabled (partitions allowed).
|
||||
- **churn-mixed**: Mixed transports on one mesh (60% UDP, 20% Ethernet, 20%
|
||||
TCP). Netem mutates 30% of links every 20-45s between normal and degraded
|
||||
policies; link flaps (max 3 down, 10-30s, connectivity protected); node
|
||||
churn (max 5 down, 30-90s, partitions allowed); bandwidth tiers
|
||||
(1/10/100/1000 Mbps). Carries baseline assertions, so a run in which the
|
||||
mesh never formed cannot report success. Local CI runs it as
|
||||
`churn-mixed --nodes 10 --duration 120`, which is the invocation its
|
||||
thresholds are calibrated for.
|
||||
- **maelstrom**: The same stressors plus peer-level topology mutation
|
||||
(connect/disconnect every 8-12s) and ephemeral identities on half the
|
||||
nodes, with `coord_ttl_secs: 10` so coordinate cache entries expire during
|
||||
the run. Tests re-convergence when the peer set and the identities behind
|
||||
it both move.
|
||||
- **maelstrom-sparse**: 50-node sparse random geometric graph (radius 0.20,
|
||||
roughly 3-4 peers per node), which forces multi-hop routing and heavy
|
||||
discovery use. The short coordinate TTL expires transit-warmed entries, so
|
||||
nodes must rediscover rather than coast on the cache.
|
||||
|
||||
### Spanning-tree and bloom propagation
|
||||
|
||||
Explicit topology with an induced parent flap. **No runner invokes this
|
||||
scenario.** It was retired from both the local and the cloud runner and is
|
||||
hand-run only; the files remain in the tree and the retirement is recorded as a
|
||||
coverage gap rather than as a migration to other tests.
|
||||
|
||||
| Scenario | Nodes | Topology | Duration | What it tests |
|
||||
| ----------- | ----- | -------- | -------- | ------------------------------- |
|
||||
| bloom-storm | 6 | explicit | 180s | Bloom rate under sustained flap |
|
||||
|
||||
- **bloom-storm**: Six-node depth-4 mesh. The two candidate uplinks at depth 2
|
||||
swap netem delay (5ms against 100ms) every 4s with parent-flap dampening
|
||||
disabled, so the node switches parents each round. Asserts a ceiling on the
|
||||
`stats.bloom.sent` delta per node over the trailing 30s, and a floor of 10
|
||||
parent switches so a harness that never produced a real switch cannot pass
|
||||
trivially. `scenarios/bloom-storm.README.md` carries the bug-class
|
||||
description and the threshold derivation.
|
||||
|
||||
### Cost-based parent selection — retired, now sans-IO unit tests
|
||||
|
||||
@@ -65,7 +98,7 @@ Explicit topologies exercising non-UDP transports.
|
||||
|
||||
| Scenario | Nodes | Transport | Shape | Duration | Netem | Link Flaps | What it tests |
|
||||
| ------------- | ----- | -------------- | ----- | -------- | ----- | ---------- | ------------------------------------------ |
|
||||
| ethernet-only | 4 | Ethernet | Ring | 90s | yes | -- | AF_PACKET transport with beacon discovery |
|
||||
| ethernet-only | 4 | Ethernet | Ring | 30s | yes | -- | AF_PACKET transport with beacon discovery |
|
||||
| ethernet-mesh | 6 | UDP + Ethernet | Mesh | 120s | yes | yes | Mixed UDP/Ethernet, netem mutation + flaps |
|
||||
| tcp-mesh | 6 | UDP + TCP | Mesh | 120s | yes | yes | Mixed UDP/TCP, netem mutation + flaps |
|
||||
|
||||
@@ -137,27 +170,32 @@ scenario runs.
|
||||
| `--duration secs` | Override the scenario's duration |
|
||||
| `--list` | List available scenarios |
|
||||
|
||||
The scenario argument accepts either a name (`churn-10`) or a file
|
||||
path (`scenarios/churn-10.yaml`).
|
||||
The scenario argument accepts either a name (`churn-mixed`) or a file
|
||||
path (`scenarios/churn-mixed.yaml`). `--list` prints the names that
|
||||
resolve.
|
||||
|
||||
## Scenario YAML Format
|
||||
|
||||
Annotated example based on `churn-10.yaml`:
|
||||
Annotated example based on `churn-mixed.yaml`:
|
||||
|
||||
```yaml
|
||||
scenario:
|
||||
name: "churn-10"
|
||||
name: "churn-mixed"
|
||||
seed: 42 # deterministic RNG seed
|
||||
duration_secs: 600 # total simulation time
|
||||
|
||||
topology:
|
||||
num_nodes: 10
|
||||
algorithm: random_geometric # or erdos_renyi, chain
|
||||
num_nodes: 20
|
||||
algorithm: erdos_renyi # or random_geometric, chain, explicit
|
||||
params:
|
||||
radius: 0.5 # algorithm-specific parameter
|
||||
p: 0.3 # algorithm-specific parameter
|
||||
ensure_connected: true # retry until graph is connected
|
||||
subnet: "172.20.0.0/24"
|
||||
subnet: "172.20.0.0/16"
|
||||
ip_start: 10 # first node gets .10
|
||||
transport_mix: # fraction of edges per transport
|
||||
udp: 0.6
|
||||
ethernet: 0.2
|
||||
tcp: 0.2
|
||||
|
||||
netem:
|
||||
enabled: true
|
||||
@@ -180,33 +218,44 @@ netem:
|
||||
link_flaps:
|
||||
enabled: true
|
||||
interval_secs: { min: 30, max: 60 }
|
||||
max_down_links: 2
|
||||
max_down_links: 3
|
||||
down_duration_secs: { min: 10, max: 30 }
|
||||
protect_connectivity: true # never partition the graph
|
||||
|
||||
traffic:
|
||||
enabled: true
|
||||
max_concurrent: 3
|
||||
interval_secs: { min: 10, max: 30 }
|
||||
duration_secs: { min: 5, max: 15 }
|
||||
max_concurrent: 10
|
||||
interval_secs: { min: 0, max: 30 }
|
||||
duration_secs: { min: 5, max: 90 }
|
||||
parallel_streams: 4
|
||||
|
||||
node_churn:
|
||||
enabled: true
|
||||
interval_secs: { min: 60, max: 180 }
|
||||
max_down_nodes: 1
|
||||
interval_secs: { min: 60, max: 90 }
|
||||
max_down_nodes: 5
|
||||
down_duration_secs: { min: 30, max: 90 }
|
||||
protect_connectivity: true # never kill the last path
|
||||
protect_connectivity: false # partitions allowed
|
||||
|
||||
bandwidth:
|
||||
enabled: false # per-link HTB rate limiting
|
||||
enabled: true # per-link HTB rate limiting
|
||||
tiers_mbps: [1, 10, 100, 1000] # each link randomly assigned a tier
|
||||
|
||||
assertions: # evaluated after the run
|
||||
baseline:
|
||||
min_nodes_reporting: 10
|
||||
max_roots: 6
|
||||
min_nodes_parented: 4
|
||||
min_sessions: 10
|
||||
|
||||
logging:
|
||||
rust_log: "debug"
|
||||
output_dir: "./sim-results"
|
||||
```
|
||||
|
||||
The assertion thresholds in the shipped file are calibrated against
|
||||
recorded runs at the invocation CI uses, and the file's own comments say
|
||||
what they were derived from. Read those before retuning them.
|
||||
|
||||
## Topology Algorithms
|
||||
|
||||
| Algorithm | Parameters | Description |
|
||||
|
||||
Reference in New Issue
Block a user