Commit Graph

167 Commits

Author SHA1 Message Date
Johnathan Corgan
c086ee3edf Add build version metadata, changelog, and version display
Embed git commit hash, dirty flag, and target triple in all binaries
via a zero-dependency build.rs. Wire clap short/long version output
so -V shows "0.1.0 (rev abc1234)" and --version adds the target
triple. Log version at daemon startup.

Add version field to show_status control socket API response. Show
the daemon's version in fipstop's tab bar title and Runtime section.

Add CHANGELOG.md in Keep a Changelog format with the 0.1.0-alpha
release (2026-02-24) and unreleased work since then.
2026-03-08 02:19:33 +00:00
Johnathan Corgan
bf117df0ca Add periodic Noise rekey with fresh DH for forward secrecy (FMP + FSP)
Implement periodic full rekey at both protocol layers using fresh DH
key exchanges. Uses the existing K-bit flag (FLAG_KEY_EPOCH /
FSP_FLAG_K) to coordinate cutover between peers.

FMP layer (IK pattern):
- ActivePeer gains rekey state: pending/previous sessions, K-bit epoch
  tracking, drain window, dampening timer
- Handshake state stored on ActivePeer with msg1 sent on existing link
- Encrypted frame handler detects K-bit flips, promotes pending
  sessions, falls back to previous session during drain
- Handshake handlers distinguish rekey from new connections using
  addr_to_link lookup with identity-based fallback
- Free all session indices (current, rekey, pending, previous) on
  peer removal

FSP layer (XK pattern):
- SessionEntry gains parallel rekey fields with XK-specific state
  for the 3-message handshake
- Route availability check before FSP rekey initiation
- Encrypted session handler adds K-bit flip detection and dual-session
  decrypt fallback
- SessionSetup/Ack/Msg3 handlers extended for rekey paths

Defense-in-depth:
- Consecutive decryption failure detector (threshold=20) triggers
  forced peer removal instead of waiting for link-dead timeout
- Identity-based rekey detection as fallback when addr_to_link
  doesn't match (e.g., TCP ephemeral ports)

Configuration: RekeyConfig with enabled flag, after_secs (default 120),
and after_messages (default 65536) thresholds.

Logging: info for successful K-bit cutover completions, warn for
failures, debug for intermediate handshake steps, trace for routine
operations (resends, drain cleanup).

Rekey lifecycle:
1. Timer/counter fires -> initiator starts new handshake
2. Old session continues handling traffic during handshake
3. Handshake completes -> initiator cuts over, flips K-bit
4. Responder sees flipped K-bit -> promotes new session
5. Both keep old session for 10s drain window
6. After drain, old session discarded

Integration test: Docker-based multi-phase test exercising both FMP
and FSP rekey with aggressive timers (35s). Verifies connectivity
across all 20 directed pairs survives two consecutive rekey cycles.
Includes rekey topology, docker-compose profile, and CI matrix entry.

Increase ping test convergence wait from 3s to 5s for CI reliability.
2026-03-07 18:33:27 +00:00
Johnathan Corgan
392572f821 Update GitHub references from jmcorgan/fips to fips-network/fips
Moved repository to the fips-network organization. Updated repository
URL in Cargo.toml, clone URLs in README.md and CONTRIBUTING.md.
2026-03-07 17:11:59 +00:00
Johnathan Corgan
74e9d465a8 Add packaging subsystem with systemd tarball installer
Packaging directory structure:
- packaging/common/ — shared config (fips.yaml) used by all formats
- packaging/systemd/ — systemd-specific installer and service units

Systemd packaging includes:
- build-tarball.sh: builds release binaries and creates a self-contained
  install tarball with stripped binaries
- fips.service: systemd unit running the daemon with security hardening
- fips-dns.service: oneshot unit configuring resolvectl to route .fips
  domain queries to the FIPS DNS shim on 127.0.0.1:5354
- install.sh: deploys binaries to /usr/local/bin, installs systemd units,
  creates fips group for non-root control socket access
- uninstall.sh: removes service and binaries, optional --purge for
  config and identity key files
- README.install.md: installation and configuration guide

Default config enables UDP (2121), TCP inbound (8443), TUN, and DNS
resolver. Identity is ephemeral by default for privacy; operators can
uncomment persistent: true to maintain a stable npub for static peer
publishing. Ethernet transport is commented out for per-node setup.
2026-03-06 21:44:58 +00:00
Johnathan Corgan
79feb41a88 Update docs for persistent identity, ECN, and multi-transport
- fips-configuration.md: add node.identity.persistent parameter and
  three-tier identity resolution documentation
- fips-intro.md: update ECN description from "reserves space" to
  reflect implemented hop-by-hop CE signaling
- README.md: update transport list (UDP, TCP, Ethernet), add
  persistent identity mention
2026-03-06 21:20:16 +00:00
Johnathan Corgan
e99654d464 Add identity provisioning with persistent key file
Implement identity management for the FIPS daemon with two modes:

Ephemeral (default): A fresh keypair is generated on every start.
Key files (fips.key, fips.pub) are written for operator visibility
but overwritten on each restart. This is privacy-friendly and
requires no configuration.

Persistent (opt-in via `node.identity.persistent: true`): Uses
three-tier identity resolution:
1. Explicit nsec in config file (advanced users)
2. Persistent key file alongside config (reused across restarts)
3. Generate new keypair, persist to key file

Key files follow the SSH id_ed25519/id_ed25519.pub convention:
- fips.key: bare bech32 nsec string, mode 0600
- fips.pub: bare bech32 npub string, mode 0644

The daemon always writes fips.pub on startup so operators can
find their current identity via `cat /etc/fips/fips.pub`.

Identity resolution extracted into testable `resolve_identity()`
function in config module. Graceful degradation: key file write
failure silently falls back to ephemeral identity.

Add `fipsctl keygen` subcommand for manual key generation:
- `-d <dir>` output directory (default: /etc/fips)
- `-f` force overwrite existing files
- `-s` print nsec/npub to stdout instead of writing files
- Warns that `persistent: true` must be set in config
- Works without a running daemon

Includes unit tests for key file read/write roundtrip, file
permissions, whitespace trimming, empty file error, path derivation,
ephemeral-by-default behavior, ephemeral key cycling, persistent
key file loading, and persistent generate-and-reuse lifecycle.
2026-03-06 20:33:52 +00:00
Johnathan Corgan
920d93571a Fix link-dead detection skipping peers that never send data
The link-dead check required last_recv_time to be Some, so peers that
completed a handshake but never sent any data back (last_recv_time =
None) were silently skipped and lived forever as zombies. Fall back to
session_start when no frame has ever been received.
2026-03-06 20:05:41 +00:00
Johnathan Corgan
56d39f223b Add ECN congestion signaling and transport congestion detection
Implement hop-by-hop ECN congestion signaling through the FMP layer,
transport-level congestion detection via kernel drop counters, and
chaos harness integration for end-to-end validation.

FMP/session ECN plumbing:

- Thread ce_flag parsed at link layer through dispatch_link_message,
  handle_session_datagram, handle_session_payload, and
  handle_encrypted_session_msg to session delivery
- Replace hardcoded false in session-layer record_recv() with actual
  ce_flag, activating ecn_ce_count tracking in session MMP

ECN congestion detection and CE relay:

- Add EcnConfig (node.ecn.*) with configurable loss_threshold (5%)
  and etx_threshold (3.0) for transit congestion detection
- Add send_encrypted_link_message_with_ce() that ORs FLAG_CE into FMP
  header flags; original method delegates with ce_flag=false
- Compute outgoing_ce = incoming_ce || local congestion on next-hop
  link, enabling hop-by-hop CE relay through transit nodes

IPv6 ECN-CE marking:

- Mark ECN-CE (0b11) in IPv6 Traffic Class on received DataPackets
  before TUN delivery when FMP CE flag is set
- Only marks ECN-capable packets (ECT(0)/ECT(1)); Not-ECT packets
  unchanged per RFC 3168

Transport congestion abstraction and UDP kernel drop detection:

- Add TransportCongestion struct to transport layer for transport-
  agnostic local congestion indicators
- Replace tokio::UdpSocket with AsyncFd<socket2::Socket> using
  libc::recvmsg() with ancillary data parsing
- Enable SO_RXQ_OVFL for kernel receive buffer drop counter on every
  packet, wiring up previously-stubbed UdpStats.kernel_drops
- Add TransportDropState for per-transport delta tracking with 1s
  tick sampling via sample_transport_congestion()
- Extend detect_congestion() with transport kernel drop check
  alongside MMP loss metrics

Congestion monitoring and control:

- Add CongestionStats (ce_forwarded, ce_received, congestion_detected,
  kernel_drop_events) to NodeStats with snapshot serialization
- Wire counters into forwarding path, session handler, and transport
  drop sampling with rate-limited warn logging (5s interval)
- Expose congestion data in show_routing control query and
  ecn_ce_count in show_mmp peer entries
- Add congestion counters to fipstop routing tab in two-column layout

Chaos harness integration:

- Add query_routing(), query_transports(), snapshot_all_congestion()
  to chaos control module
- Add congestion/kernel-drop log analysis in logs module
- Add congestion-stress scenario: 10-node tree, 1 Mbps bandwidth,
  5-10% netem loss, heavy iperf3 traffic
- Add IngressConfig for tc ingress policing with per-peer policer
  filters simulating upstream bandwidth bottlenecks
- Add iperf3 JSON result capture to traffic manager for throughput
  measurement across scenarios
- Add ECN A/B test scenarios (ecn-ab-on/off.yaml) with ingress
  policing and comparison script
- Enable TCP ECN negotiation (tcp_ecn=1 sysctl) in container
  entrypoint for end-to-end CE propagation

Tests:

- 10 ECN unit/integration tests: mark_ipv6_ecn_ce variants, CE relay
  chain (3-node propagation), EcnConfig serde roundtrip
- 3 transport drop congestion detection unit tests

Documentation:

- Update fips-mesh-layer.md: replace outdated CE Echo stub with full
  ECN Congestion Signaling section covering detection logic, CE relay,
  IPv6 marking, session tracking, and monitoring counters
- Update fips-configuration.md: add node.ecn.* parameter table and
  ecn block in complete reference YAML
- Update fips-transport-layer.md: add Congestion Reporting section
  with TransportCongestion struct, congestion() trait method, and
  per-transport status; document AsyncFd/recvmsg/SO_RXQ_OVFL in UDP
- Update chaos README: add congestion/ECN scenario docs, ingress
  traffic control, and iperf3 JSON capture sections
- Update README.md: add ECN to features list and "What works today";
  update transport and tooling entries
2026-03-05 17:05:57 +00:00
Johnathan Corgan
6be05f0a0a Add FMP version check to TCP stream reader
Non-FIPS clients (e.g., TLS connections) hitting the TCP listen port
produce misleading "unknown FMP phase" errors because the stream reader
checked phase before version. A TLS ClientHello (byte 0x16) parsed as
version=1, phase=6.

Add UnknownVersion error variant and check the version nibble before
phase dispatch, so non-FIPS connections now report "unknown FMP version: 1"
instead of "unknown FMP phase: 0x06".
2026-03-05 15:17:47 +00:00
Johnathan Corgan
48464e63c1 Fix Ethernet AEAD decryption failures caused by minimum-frame padding 🤦
Ethernet requires a minimum 46-byte payload (60-byte frame minus
14-byte header). NICs/drivers pad shorter frames with zeros. The
Ethernet transport had no length field, so the receiver included
padding bytes in the ciphertext, causing AEAD (ChaCha20-Poly1305)
authentication tag mismatch on small frames like heartbeats (39 bytes
with prefix, padded to 46).

Add a 2-byte little-endian payload length field after the frame type
byte. Wire format changes from [type:1][payload] to
[type:1][length:2 LE][payload]. The receiver uses the length field to
extract exactly the right number of bytes, ignoring any NIC padding.

Frame overhead increases from 1 to 3 bytes, effective MTU adjusted
accordingly. This is a wire-format breaking change requiring
simultaneous upgrade of all Ethernet peers.
2026-03-05 14:34:30 +00:00
Johnathan Corgan
70e365b14d Fix PMTUD: per-destination path MTU check and ICMPv6 MTU field width
Two bugs prevented Path MTU Discovery from working across heterogeneous
links (e.g., ethernet→UDP boundary):

1. ICMPv6 Packet Too Big wrote the MTU as u16 into a 32-bit field
   (RFC 4443 §3.2), causing the kernel to read an inflated value.
   Fixed by changing build_packet_too_big() to use u32 and writing
   all 4 bytes.

2. handle_tun_outbound() only checked the local transport MTU, not
   the per-destination PathMtuState updated by MtuExceeded signals.
   Added a PathMtuState check after session lookup so subsequent
   oversized packets generate ICMPv6 PTB on TUN instead of being
   forwarded and dropped at the bottleneck hop.

Added integration test exercising the full PMTUD loop across a 3-node
chain with heterogeneous MTUs: oversized packet → forwarding failure →
MtuExceeded signal → PathMtuState update → ICMPv6 PTB on TUN.
2026-03-04 03:34:41 +00:00
Johnathan Corgan
77ac8c822e Add fipstop TUI monitoring tool with smoothed metrics and quality indices
fipstop: ratatui-based TUI for real-time monitoring of a running FIPS daemon.

Tabs and navigation:
- 8 navigable tabs: Node, Peers, Transports, Sessions, Tree, Filters,
  Performance, Routing
- Tab/BackTab navigation with group separators in tab bar
- Table views with selectable rows, detail drill-down panels, and scrollbars

Node tab:
- Runtime info: pid, exe path, uptime, control socket path, TUN adapter name
- Identity: npub, node_addr, ipv6 address
- State summary with peer/session/link/transport/connection counts
- TUN IPv6 traffic and forwarded transit traffic counters

Peers tab:
- Table with Name, Address, Conn, Depth, SRTT, Loss, LQI, Pkts Tx/Rx
- Detail panel: identity, connection info, transport cross-reference,
  tree/bloom state, link stats, MMP metrics with LQI

Sessions tab:
- Table with Name, Remote Addr, State, Role, SRTT, Loss, SQI, Path MTU,
  Last Activity
- Detail panel: identity, session info, traffic stats, MMP metrics with SQI

Transports tab:
- Hierarchical tree view: expandable transport parents with nested links
  (▼/▶ indicators, ├─/└─ tree chars, Space/Arrow to expand/collapse)
- Transport detail: type-specific stats (UDP/TCP/Ethernet)
- Link detail: peer cross-reference with MMP metrics and LQI

Performance tab:
- Link-layer MMP: SRTT, loss, ETX, LQI, goodput per peer
- Session-layer MMP: SRTT, loss, ETX, SQI, path MTU per session
- Trend indicators (rising/falling/stable) with context-aware coloring

Routing tab:
- Routing state: cache sizes, pending lookups, recent requests
- Coordinate cache: entries, fill ratio, TTL, expiry, avg age
- Statistics: forwarding, discovery request/response, error signal counters

Tree tab:
- Spanning tree position with 16 announce stats (inbound/outbound/cumulative)

Filters tab:
- Bloom filter announce stats, per-peer fill ratio and estimated node count

MMP metrics enhancements:
- Add etx_trend DualEwma for smoothed ETX tracking
- Add smoothed_loss() and smoothed_etx() accessors (long-term EWMA)
- LQI (Link Quality Index) = smoothed_etx * (1 + srtt_ms / 100)
- SQI (Session Quality Index) = same formula for session layer
- All loss/ETX displays prefer smoothed values with raw fallback

Control socket:
- Add smoothed_loss, smoothed_etx, lqi/sqi to show_peers, show_sessions,
  and show_mmp JSON responses
- Rename fips_address to ipv6_addr in show_status and show_peers
- Add tun_name and control_socket to show_status
- FHS-compliant 3-tier default path: $XDG_RUNTIME_DIR, /run/fips, /tmp

Node extensions:
- Add started_at/uptime() to Node
- Add tun_name() accessor

Docker sidecar updates:
- TCP transport support via FIPS_PEER_TRANSPORT env var
- Build scripts include fipstop binary
2026-03-01 16:33:33 +00:00
Johnathan Corgan
71a5c68fa9 Implement comprehensive node and transport statistics
Add 71 new counters (84 values) across three categories:

Node statistics (NodeStats, plain u64 — single handler context):
- Forwarding: 9 counters x (packets + bytes) = 18 values. Covers
  received, decode_error, ttl_exhausted, delivered, forwarded,
  drop_no_route, drop_mtu_exceeded, drop_send_error, originated.
- Discovery: 17 counters (packets only). Request path: received,
  decode_error, duplicate, already_visited, target_is_us, forwarded,
  ttl_exhausted, initiated, deduplicated. Response path: received,
  decode_error, forwarded, identity_miss, proof_failed, accepted,
  timed_out.
- Error signals: 3 counters — coords_required, path_broken,
  mtu_exceeded.
- Spanning tree: 16 counters. Inbound announce handling (received
  through accepted, parent switch, loop detection, ancestry change),
  outbound (sent, rate limited, send failed), cumulative events
  (parent switches/losses, flap dampening).
- Bloom filter: 10 counters. Inbound (received through accepted),
  outbound (sent, debounce suppressed, send failed).

Transport statistics (AtomicU64 + Arc — shared with spawned tasks):
- UDP (6 counters, 8 values): packets/bytes sent/recv, send_errors,
  recv_errors, mtu_exceeded, kernel_drops (stub for SO_MEMINFO).
- TCP (10 counters, 12 values): packets/bytes sent/recv, send_errors,
  recv_errors, mtu_exceeded, plus connection lifecycle counters
  (established, accepted, rejected, timeouts, refused).

Control socket integration:
- show_routing: forwarding, discovery, error signal stats
- show_tree: spanning tree stats + per-peer bloom metrics
  (estimated_count, set_bits, fill_ratio) and coordinate paths
- show_bloom: bloom filter stats + per-peer snapshots
- show_transports: per-transport stats snapshots

Also refactor UDP transport from flat files (udp.rs + udp_stats.rs)
into directory module (udp/mod.rs + udp/stats.rs) matching TCP
structure, and fix pre-existing clippy warnings in tree/tests.rs.
2026-02-28 18:23:04 +00:00
Johnathan Corgan
5c1cbb4c30 Fix spanning tree coordinate loop: reject parents whose ancestry contains us
evaluate_parent() did not check whether a candidate peer's ancestry
path already contained our own node_addr. Two nodes (e.g., sidecar and
VPS) could each select the other as parent, creating an alternating
coordinate loop that grew unbounded on each TreeAnnounce exchange.

Add loop detection in two places:
- evaluate_parent(): skip candidates whose ancestry contains us
- handle_tree_announce(): detect when current parent's updated ancestry
  contains us and drop the parent instead of propagating the loop
2026-02-28 13:21:44 +00:00
Johnathan Corgan
32054271f5 Fix clippy warnings in test code
Replace assert_eq! with literal bool, collapse nested if-let,
use iterators instead of indexed loops, use slice instead of vec.
2026-02-27 15:03:53 +00:00
Johnathan Corgan
9668807ca4 Add Docker sidecar deployment for FIPS
Tailscale-style sidecar pattern: a FIPS container provides mesh
networking, and a companion app container shares its network namespace
via network_mode: service:fips.

Security model:
- iptables enforces strict isolation — the app container can only
  communicate over the FIPS mesh (fd::/8 via fips0)
- No IPv4 access: eth0 restricted to FIPS UDP transport (port 2121)
- No IPv6 on eth0: ip6tables blocks all eth0 IPv6 traffic
- Only fips0 and loopback are reachable from the app container

The sidecar accepts peer configuration via environment variables
(FIPS_NSEC, FIPS_PEER_NPUB, FIPS_PEER_ADDR), so it can be pointed
at any FIPS node without config file generation.

Files:
- testing/sidecar/: Dockerfile, Dockerfile.app, docker-compose.yml,
  entrypoint.sh, .env, resolv.conf, scripts/build.sh
- testing/sidecar/README.md: security model, quick-start, architecture,
  DNS resolution, troubleshooting, production considerations
- testing/sidecar/scripts/test-sidecar.sh: 3-node chain integration
  test verifying link establishment, multi-hop connectivity, and
  network isolation on each app container
- .github/workflows/ci.yml: sidecar integration test matrix entry
2026-02-27 03:02:14 +00:00
Johnathan Corgan
e5b4f1a88a Remove unused root_idx in bloom filter split-horizon test 2026-02-27 02:38:36 +00:00
origami74@gmail.com
8336b1d2d5 ci: expand integration matrix, add nextest JUnit reporting, workflow_dispatch 2026-02-26 22:04:14 -03:00
Johnathan Corgan
cd9289e0f3 Update test harness docs and add transport_mix support
Chaos harness enhancements:
- transport_mix config: weighted random transport assignment for random
  topologies (erdos_renyi, random_geometric) with UDP/Ethernet/TCP
- Replace LoRa with Bluetooth L2CAP in cost-based/mixed-tech scenarios
  using realistic netem values (15-40ms delay, 5-15ms jitter, 2-8% loss)
- New churn-20-mixed scenario: 20-node Erdos-Renyi with 60% UDP,
  20% Ethernet, 20% TCP, full netem/link-flap/churn/bandwidth config
- Expanded chaos README with full scenario catalog in four categories

Static harness:
- Updated README with topology table and scenario count
2026-02-27 00:52:17 +00:00
Johnathan Corgan
ec64a0dce1 Add TCP transport implementation and test harness support
Implement TCP transport for FIPS enabling firewall traversal and serving
as the foundation for future Tor transport. This is the first
connection-oriented transport in the system.

Key design decisions:
- FMP header-based framing: reuses existing 4-byte FMP common prefix for
  packet boundary recovery with zero framing overhead
- Session survives TCP reconnection: Noise/MMP/FSP state bound to npub,
  not TCP connection; MMP liveness is sole authority for peer death
- Connect-on-send: fresh connection on first send, transparent reconnect
- close_connection() trait method for cross-connection deduplication cleanup

New transport files:
- src/transport/tcp/mod.rs: TcpTransport, connection pool, accept loop
- src/transport/tcp/stream.rs: FMP-aware stream reader (shared with Tor)

Modified: transport trait (close_connection), TcpConfig, TransportHandle
match arms, create_transports(), initiate_connection() for connection-
oriented links, cross-connection tie-breaker cleanup, design docs.

Tree announce loop and TCP stability fixes:
- Preserve tree announce rate-limit state across reconnection: carry
  forward last_tree_announce_sent_ms when a peer reconnects so the
  rate-limit window isn't reset to zero
- Drop oversize TCP packets at sender: pre-send MTU check returns
  MtuExceeded instead of writing to the stream, preventing receiver-side
  connection teardown and reset-reconnect cycles

Chaos harness:
- TCP transport support: tcp_edges/has_tcp/tcp_peers in SimTopology,
  transport-aware config_gen with per-edge transport type, TCP port 443,
  pure-TCP node support
- Include all non-Ethernet edges in directed_outbound()
- Fix netem/links log messages to say "IP-based" instead of "UDP"
- Add tcp-chain, tcp-only, and tcp-mesh scenario files

Static harness:
- Transport-aware config generation (get_default_transport, transport_port)
- TCP transport injection via Python post-processing
- Add tcp-chain topology and docker-compose profile
2026-02-27 00:41:37 +00:00
Johnathan Corgan
c48b7aec5a Fix stale veth socket in Ethernet beacon sender
When a peer container is restarted during node churn, the veth pair is
destroyed and recreated. The beacon sender's AF_PACKET socket becomes
stale, producing ENXIO (os error 6) on every send with no recovery.

The beacon sender loop now tracks consecutive send errors and, after 3
consecutive ENXIO failures, attempts to open a fresh AF_PACKET socket
on the same interface. Only the first error in a streak is logged at
warn level to avoid log spam. On successful reopen, beacons resume
normally, allowing peer rediscovery.

Also adds reopen_beacon_socket() helper that creates and wraps a new
PacketSocket without needing access to the EthernetTransport struct.
2026-02-26 21:37:14 +00:00
Johnathan Corgan
daf1e629df Change default UDP port to 2121 and EtherType to 0x2121
Update the default UDP bind port from 4000 to 2121 (decimal) and the
default Ethernet EtherType from 0x88B5 to 0x2121 across all source
code, documentation, configuration templates, test fixtures, and
scripts. Remove references to "IEEE 802 experimental range" since
0x2121 is not in that range.
2026-02-26 13:22:09 +00:00
origami74@gmail.com
e0fa69e0b9 ci: add GitHub Actions workflow
Three-job pipeline:
- build: matrix over ubuntu/macos/windows, native cargo build per runner;
  macOS and Windows are continue-on-error (best-effort). Uploads Linux
  binary as artifact for downstream jobs.
- test: cargo test --all on ubuntu, gated on build succeeding.
- integration: parallel matrix of static-mesh, static-chain, and
  chaos-smoke-10; runs only when build + test both pass. Static jobs
  use docker compose + ping-test.sh; chaos job runs the stochastic
  simulation via chaos.sh.
2026-02-26 11:52:10 +00:00
Arjen
c1589c02af Cross-compilation and macOS compatibility fixes (#1)
- Ethernet ioctl: cfg-gate the ioctl request parameter type — c_int on
  musl, c_ulong on gnu — so the same code compiles on both
  x86_64-unknown-linux-gnu and x86_64-unknown-linux-musl targets

- Chaos harness macOS support: replace direct host 'ip link' invocations
  with a privileged Docker container helper (--net=host --pid=host) that
  shares the Docker VM's namespaces, making veth pair setup work on both
  Linux and macOS (where host 'ip' is unavailable and container PIDs
  live inside the Docker Desktop VM)

- Python 3.9 compat: add 'from __future__ import annotations' to
  docker_exec.py for X|Y union syntax support

- Add deploy/ to .gitignore

Co-authored-by: origami74 <origami74@gmail.com>
2026-02-26 03:48:51 -08:00
Johnathan Corgan
d29da442ac Add Ethernet transport with beacon discovery
Implement raw Ethernet transport using AF_PACKET SOCK_DGRAM on Linux
with EtherType 0x88B5 (IEEE experimental range) and 1-byte frame type
prefix (0x00=data, 0x01=beacon).

Transport implementation:
- EthernetConfig with interface, ethertype, MTU, buffer sizes, and
  four independent discovery knobs (discovery, announce, auto_connect,
  accept_connections)
- PacketSocket/AsyncPacketSocket wrappers with ioctl helpers for
  interface index, MAC address, and MTU queries
- EthernetTransport with Transport trait impl, async start/stop/send,
  receive loop dispatching data frames and discovery beacons
- Discovery beacons (34 bytes: type + version + x-only pubkey) with
  DiscoveryBuffer for peer accumulation and dedup
- Atomic statistics counters (frames, bytes, errors, beacons)
- Platform-gated with #[cfg(target_os = "linux")]

Transport-layer discovery integration:
- Promote auto_connect() and accept_connections() to Transport trait
  with default implementations and TransportHandle dispatch
- Extract initiate_connection() so both static peer config and
  discovery auto-connect share the same handshake initiation path
- Add poll_transport_discovery() to the tick handler to drain
  discovery buffers and auto-connect to discovered peers
- Enforce accept_connections() in handle_msg1() — transports with
  accept_connections=false silently drop inbound handshakes

Node integration:
- create_transports() handles Ethernet named instances
- resolve_ethernet_addr() parses "interface/mac" address format
- transport_mtu() generalized for multi-transport operation

Test harness:
- VethPair RAII struct for veth pair lifecycle management
- Three #[ignore] integration tests requiring root/CAP_NET_RAW:
  two-node handshake, data exchange, mixed transport coexistence
- Chaos harness: transport-aware topology model, VethManager for
  veth pairs between Docker containers, Ethernet-aware config gen,
  netem split (HTB+u32 for UDP, root netem for veth), transport-aware
  link flaps and node churn with veth re-setup
- Container entrypoint waits for configured Ethernet interfaces
  before starting FIPS (handles veth creation timing)
- New scenarios: ethernet-only (4-node ring), ethernet-mesh (6-node
  mixed UDP+Ethernet with netem and link flaps)

Documentation:
- fips-transport-layer.md: Ethernet section, beacon discovery, WiFi
  compatibility, updated discovery state, trait surface additions,
  implementation status table
- fips-configuration.md: Ethernet parameter table, named instances,
  peer address format, mixed UDP+Ethernet example, complete reference
- fips-wire-formats.md: Ethernet frame type prefix note
2026-02-26 00:03:14 +00:00
Johnathan Corgan
7260ad2878 Improve README and enable DNS resolver by default
Rework top-level README: add badges, status/roadmap section, config
search path docs, minimal two-node example with transports, DNS setup
instructions with systemd-resolved and resolv.conf examples, and
connectivity test walkthrough.

Replace ASCII document-relationship diagram with SVG in design docs.

Change DNS resolver default from disabled to enabled (port 5354).
Update config merge to allow higher-priority configs to disable it.
2026-02-24 18:58:32 +00:00
Johnathan Corgan
27da3c0bbf Add fipsctl to Docker test harness builds
Include fipsctl binary in both static and chaos test harness
Dockerfiles, build scripts, and gitignores alongside the existing
fips binary.
2026-02-24 18:18:22 +00:00
Johnathan Corgan
58664c7c77 Update dependencies: rand 0.10, rtnetlink 0.20, tun 0.8, and others
Bump rand (0.8→0.10), rtnetlink (0.14→0.20), tun (0.7→0.8),
simple-dns (0.9→0.11), socket2 (0.5→0.6), and criterion (0.5→0.8).

Migrate all rand call sites: thread_rng()→rng(), gen()→random(),
gen_range()→random_range(), RngCore→Rng trait. Work around secp256k1
0.30 requiring rand 0.8 by generating random bytes directly and
constructing SecretKey from slice.

Migrate rtnetlink to builder-based API: LinkSetRequest replaced with
LinkUnspec builder + change(), RouteAddRequest replaced with
RouteMessageBuilder.

Remove bloom benchmark (criterion 0.8 incompatible with old harness
config).
2026-02-24 18:02:06 +00:00
Johnathan Corgan
00e26765bd Design documentation illustration and review pass
Wire format diagrams:
- Add 24 SVG diagrams covering every FMP and FSP wire format: common
  prefix, established frame headers, Noise IK handshake messages,
  handshake flow, TreeAnnounce, AncestryEntry, FilterAnnounce,
  LookupRequest/Response, SessionDatagram, Disconnect, SenderReport,
  ReceiverReport, FSP complete message, SessionSetup/Ack/Msg3,
  PathMtuNotification, CoordsRequired, PathBroken, and MtuExceeded
- Replace ASCII art in fips-wire-formats.md with SVG references
- Apply text edits to fips-mesh-layer.md, fips-mesh-operation.md,
  fips-transport-layer.md, and fips-ipv6-adapter.md

Spanning tree dynamics:
- Add 12 topology SVG diagrams: node join (overview + 3-panel steps),
  three-node convergence (4-panel), link addition with depth labels,
  link removal, partition formation, and 6 real-world example diagrams
  (office, mixed-link, two-site WAN topologies)
- Rewrite all code blocks to narrative prose with diagram references
- Add inline prior art attributions distinguishing Yggdrasil-derived
  concepts from FIPS-novel contributions
- Add 3 new references (De Couto ETX, IEEE 802.1D, RFC 2328 OSPF) and
  Prior Art summary
- Remove outdated sections: indirect partition note, integration test
  gaps, DHT-based lookup reference
- Change "must elect a new root" to "must rediscover its new root"

Spanning tree design review (fips-spanning-tree.md):
- Rename "Root Election" to "Root Discovery" across docs
- Add "What Is a Spanning Tree?" introductory section
- Add parent selection intro explaining self-organization role
- Fix tree distance example: 4 hops, not 2
- Clarify timestamp field as advisory only
- Remove unimplemented ROOT_TIMEOUT and TREE_ENTRY_TTL from timing
  parameters and implementation status tables

Bloom filter design review (fips-bloom-filters.md):
- Add "What Is a Bloom Filter?" intro section
- Rewrite Purpose section to frame filters as routing path identification
- Correct FPR analysis (old values were 3-50x overstated)
- Add Filter Occupancy Model based on network size and tree position
- Fix filter expiration to describe actual MMP-based cleanup
- Combine Scale Considerations with Size Classes after Wire Format
- Fix stale FPR values in src/bloom/mod.rs comments

Session layer review (fips-session-layer.md):
- Add inline prior art attributions: Noise Protocol Framework, WireGuard,
  DTLS (RFC 6347), IKEv2 (RFC 7296), RFC 1191 PMTUD, Yggdrasil, NIP-44
- Replace warmup state machine ASCII art with SVG diagram
- Convert CoordsWarmup wire format code block to prose
- Add External References section with full citations

Level 5 implementation doc cleanup:
- Delete fips-software-architecture.md (redundant with protocol layer docs)
- Delete fips-state-machines.md (Rust tutorial, not protocol design)
- Add fipsctl command reference to README.md
- Update cross-references in fips-intro.md, docs/design/README.md,
  fips-transport-layer.md, fips-configuration.md

Fixes:
- Correct fd::/8 to fd00::/8 in fips-session-layer.md,
  fips-identity-derivation.svg, and fips-node-architecture.svg
- Fix config example MTU: 1197 → 1472 in fips-configuration.md

File organization:
- Move all SVG diagrams into docs/design/diagrams/ subdirectory
- Update all diagram references to use new paths
2026-02-24 17:32:37 +00:00
Johnathan Corgan
c1820e689d Update design docs for replay suppression, MMP backoff, and flap dampening
- fips-configuration.md: add flap_threshold, flap_window_secs,
  flap_dampening_secs to tree config table and YAML reference
- fips-spanning-tree.md: add flap dampening to Stability Mechanisms,
  Timing Parameters, and Implementation Status tables
- spanning-tree-dynamics.md: update Known Limitations to reflect flap
  dampening implementation, revise impact text
- fips-session-layer.md: add Send Failure Backoff subsection under
  Session-Layer MMP
- fips-mesh-layer.md: add log suppression note to Replay Protection
2026-02-23 20:28:17 +00:00
Johnathan Corgan
94c0e19951 Add flap dampening to parent selection
Track parent switch frequency in a sliding window. When switches exceed
a configurable threshold (default 4 in 60s), impose an extended hold-down
period (default 120s) that prevents non-mandatory parent changes.

Mandatory switches (parent loss, root change, shouldn't-be-root) bypass
dampening. The flap counter resets when the window expires naturally.

Implements TASK-2026-0030 / IDEA-0013.
2026-02-23 20:00:58 +00:00
Johnathan Corgan
5d72e7cee1 Add exponential backoff to session MMP probing on send failures
Track consecutive send failures in SenderState. Apply 2^n backoff
multiplier (capped at 32x) to the report interval. Suppress debug
logs after 3 consecutive failures, emit recovery summary on success.
2026-02-23 19:59:43 +00:00
Johnathan Corgan
05e3853ca1 Suppress repeated replay detection log messages during link transitions
Add per-peer replay suppression counter to ActivePeer. Log the first 3
replay detections at DEBUG, then suppress with a one-time notice. Emit
a summary count on session replacement or peer removal.

Non-replay decryption errors continue to be logged unconditionally.
2026-02-23 19:56:12 +00:00
Johnathan Corgan
e3b5bd0bcd Fix identity cache miss during lookup response verification
Guard discovery triggers in handle_path_broken() and
handle_coords_required() with has_cached_identity() check. When the
XK responder receives an error signal before msg3 completes, the
initiator's identity is unknown, making LookupResponse proof
verification impossible. Skip discovery in this case — the handshake
retry mechanism handles recovery.

Downgrade the identity_cache miss log from ERROR to WARN since it's a
known race condition, not a bug.
2026-02-23 18:44:27 +00:00
Johnathan Corgan
0d93a19e07 Implement cost-based parent selection with periodic re-evaluation
Cost-based parent selection:
- Replace depth-only parent selection with effective_depth = depth + link_cost
- link_cost computed from locally measured MMP metrics: etx * (1.0 + srtt_ms / 100.0)
- Prevents bottleneck subtrees in heterogeneous networks where a LoRa link
  at depth 1 would otherwise always beat fiber at depth 2
- Configurable hysteresis (default 0.2) prevents marginal parent switches
- Configurable hold-down timer (default 30s) suppresses re-evaluation
  after parent switch
- Mandatory switches (parent lost, root change) bypass both safeguards
- Link costs passed as HashMap parameter to keep TreeState pure

Periodic re-evaluation:
- evaluate_parent() was only called on TreeAnnounce receipt or parent loss;
  after tree stabilization, link degradation went undetected
- Added timer-based re-evaluation (reeval_interval_secs, default 60s) that
  calls evaluate_parent() from the tick handler with current MMP link costs
- Respects existing hold-down and hysteresis safeguards
- Short-circuits when disabled or <2 peers

Design documentation:
- Update 7 design docs to reflect cost-based parent selection
- Replace depth-only algorithm descriptions with effective_depth model
- Replace rejected cumulative path cost spec with local-only design rationale
- Rewrite Example 2 (heterogeneous links) for local-only cost model
- Update config docs: parent_switch_threshold replaced by parent_hysteresis,
  hold_down_secs, reeval_interval_secs

Chaos simulation enhancements:
- fips_overrides with deep merge for per-scenario FIPS config customization
- Explicit topology algorithm for deterministic test graphs
- Control socket querying via fipsctl for tree/MMP snapshot collection
- Edge existence validation in netem manager
- Per-link netem policy overrides
- 9 new chaos scenarios covering cost avoidance, depth-vs-cost tradeoffs,
  stability, mixed topologies, periodic re-evaluation, and bottleneck parent

12 new unit tests, 667 total passing, clippy clean.
2026-02-23 17:15:20 +00:00
Johnathan Corgan
717be3d960 Restrict bloom filter propagation to tree edges, update design docs
Gate peer_inbound_filters() to only collect from tree peers (parent
and children), so outgoing filter computation merges only tree-sourced
information. All peers still receive FilterAnnounce messages and store
filters locally for routing queries — the restriction is only on what
gets merged into outgoing filters.

This prevents bloom filter saturation where mesh shortcuts cause every
node's filter to converge toward the full network. With tree-only
merge, filters contain subtree (from children) + complement (from
parent) + single-hop mesh views.

Implementation:
- Add is_tree_peer() helper to determine tree parent/child relationship
- Gate peer_inbound_filters() to tree peers only (single control point)
- Trigger bloom filter exchange on tree relationship changes
- Add est_entries, set_bits, fill ratio, and tree_peer fields to
  FilterAnnounce send/receive debug logs
- Add test_bloom_filter_split_horizon test verifying directional
  asymmetry: upward filters contain only the child's subtree, downward
  filters contain only the complement
- Add print_filter_cardinality diagnostic helper for test inspection

Design docs:
- fips-bloom-filters.md: Add directional asymmetry and mesh peer filter
  subsections, update per-peer filter model, saturation mitigation,
  implementation status table
- fips-mesh-operation.md: Update filter propagation description, add
  directional asymmetry, tree relationship change trigger
- fips-intro.md: Rewrite bloom propagation paragraph for tree-only merge
2026-02-23 14:00:00 +00:00
Johnathan Corgan
dc89edf60b Update design docs for session 142 implementation changes
Comprehensive documentation review across 12 files to reflect:
- Noise XK at FSP (was IK), 3-message handshake, SessionMsg3 wire format
- Epoch exchange in Noise handshakes for peer restart detection
- Per-link MTU, min_mtu/path_mtu in lookup packets
- MtuExceeded (0x22) error signal wire format and behavior
- LookupResponse proof now covers target_coords
- Discovery reverse-path routing as primary (not greedy)
- Control socket architecture and fipsctl binary
- FSP handshake state machine (Initiating/AwaitingMsg3/Established)
- Root timeout framing updated for heartbeat cascading
2026-02-22 23:03:00 +00:00
Johnathan Corgan
2293f7d2d5 Replace Noise IK with Noise XK at the FSP session layer
The session-layer handshake now uses the 3-message XK pattern instead
of the 2-message IK pattern, providing stronger initiator identity
hiding. The initiator static key is deferred to msg3 and encrypted
under the es+ee DH chain, so eavesdroppers cannot identify the
initiator from the handshake.

XK pattern: -> e, es (msg1) / <- e, ee + epoch (msg2) / -> s, se + epoch (msg3)

Key changes:
- Add XK handshake methods alongside existing IK methods in noise module
- Add SessionMsg3 wire format and FSP_PHASE_MSG3 (0x03) prefix
- Replace Responding state with AwaitingMsg3 in session state machine
- Rewrite session handlers: handle_session_setup defers identity to msg3,
  handle_session_ack processes msg2 and sends msg3, new handle_session_msg3
  completes the responder handshake and registers identity
- Link-layer (FMP) continues to use Noise IK unchanged
- Add comprehensive XK unit tests and update all integration tests
2026-02-22 22:05:23 +00:00
Johnathan Corgan
4ff1762434 Add MTU fields to lookup packets for path MTU discovery
Add min_mtu (u16) to LookupRequest and path_mtu (u16) to
LookupResponse, enabling the discovery system to report transport
MTU capability along the lookup path.

LookupRequest carries min_mtu (origin's minimum MTU requirement,
default 0 = no requirement). LookupResponse carries path_mtu
(initialized to u16::MAX by the target, reduced by transit nodes
via min(path_mtu, outgoing_link_mtu) on the reverse path).

path_mtu is a transit annotation like SessionDatagram.path_mtu and
is NOT included in the proof signature. The originator stores the
discovered path_mtu in CacheEntry alongside cached coordinates.

Wire format: +2 bytes each for LookupRequest and LookupResponse.
2026-02-22 21:52:08 +00:00
Johnathan Corgan
20cf6932cd Implement reactive MtuExceeded error signal (0x22)
Add a new session-layer error signal that transit routers send back to
the source when a forwarded packet exceeds the next-hop transport MTU.
This complements the existing proactive path MTU discovery (min'd at
each hop) by providing immediate feedback when oversized packets are
dropped, closing the transient window before the proactive mechanism
converges.

Wire format: 36-byte payload (msg_type + flags + dest_addr + reporter +
mtu) with FSP phase=0x0 and U flag set, matching the existing
CoordsRequired/PathBroken pattern.

Changes:
- Add SessionMessageType::MtuExceeded (0x22) and MtuExceeded struct with
  encode/decode methods to protocol/session.rs
- Add NodeError::MtuExceeded variant to propagate structured MTU info
  from TransportError through send_encrypted_link_message()
- Catch MtuExceeded in the forwarding path and send error signal back to
  the datagram source via send_mtu_exceeded_error(), rate-limited by the
  existing routing_error_rate_limiter
- Handle incoming MtuExceeded at the source by calling
  PathMtuState::apply_notification() for immediate MTU decrease
- Add unit tests for encode/decode roundtrip, boundary MTU values, and
  too-short payload rejection
2026-02-22 21:37:31 +00:00
Johnathan Corgan
557a84c12b Fix discovery response routing to use reverse-path first
The target node's send_lookup_response() was using greedy tree routing
(find_next_hop) as the primary method to route responses back to the
origin. When the target had the origin's coords cached from a prior
lookup, find_next_hop would route the response to whichever peer was
closest to the origin in tree space -- which might not have been on the
request's forward path. That peer would have no recent_requests entry
for the request_id, causing it to treat the response as if it were the
originator, fail identity_cache lookup, and discard the response.

Fix: prefer the reverse-path (recent_requests.from_peer) as the
primary routing method for the first hop, falling back to
find_next_hop only if no recent_request entry exists.

Also adds diagnostic output on failure to aid future debugging.
2026-02-22 21:37:27 +00:00
Johnathan Corgan
0a6d433d32 Add Unix domain control socket for runtime observability
Add a Unix domain socket interface for querying node state at runtime.
A spawned tokio task accepts connections and communicates with the main
event loop via mpsc/oneshot channels, keeping all Node access
single-threaded.

Includes:
- src/control/ module with socket lifecycle, JSON protocol, and 11
  query handlers (status, peers, links, tree, sessions, bloom, mmp,
  cache, connections, transports, routing)
- Separate fipsctl binary for CLI queries (fipsctl show <command>)
- ControlConfig in node configuration (enabled, socket_path)
- Integration into the main select! event loop
2026-02-22 20:53:00 +00:00
Johnathan Corgan
92d5df8037 Add open-source project scaffolding
Add standard open-source project files for public repository
presentability:

- README.md with protocol overview, Nostr identity integration,
  feature highlights, quick start, and project structure
- MIT LICENSE (Copyright 2026 Johnathan Corgan)
- CONTRIBUTING.md with guidelines for issues, PRs, and testing
- Cargo.toml metadata (description, license, authors, repository,
  readme)
- .gitignore expanded with editor/IDE patterns and .claude/
2026-02-22 20:52:55 +00:00
Johnathan Corgan
5410bf3490 Add per-link MTU support to Transport trait
Add link_mtu(&TransportAddr) method to the Transport trait with a
default implementation that falls back to the transport-wide mtu().
This enables transports like BLE to report per-connection MTU values
while maintaining backward compatibility for UDP and other transports
that use a single MTU for all links.

Update the forwarding and session send paths to query link_mtu() with
the next-hop peer's current address, falling back to transport-wide
mtu() when no address is available.
2026-02-22 20:51:55 +00:00
Johnathan Corgan
f920526ece Add epoch-based peer restart detection to Noise IK handshake
Each node generates a random 8-byte startup epoch, encrypted inside
both Noise IK handshake messages (msg1 and msg2). When a peer's msg1
arrives with a different epoch than the stored value, the node tears
down the stale session and processes the msg1 as a new connection,
enabling near-instant restart detection instead of the 30-second
dead timeout.

Wire format impact:
- msg1: 82 -> 106 bytes (added 24-byte encrypted epoch after ss DH)
- msg2: 33 -> 57 bytes (added 24-byte encrypted epoch after se DH)
- Wire msg1: 90 -> 114 bytes, wire msg2: 45 -> 69 bytes
2026-02-22 20:50:50 +00:00
Johnathan Corgan
1adfd9e90f Fix LookupResponse proof verification
Include target_coords in proof_bytes() signed data to prevent transit
nodes from substituting fake coordinates. Add mandatory signature
verification at the originator using the target's public key from
identity_cache (guaranteed available since lookups are only initiated
from contexts where the key is already cached).

Verification failure discards the response. Identity cache miss (should
never happen) logs an error and discards. Add four new tests covering
verification success, failure, cache miss, and coordinate substitution
detection.
2026-02-22 20:45:28 +00:00
Johnathan Corgan
1c3555b19e Expand fips-intro.md: prior work, MMP section, review fixes
- Expand Prior Work from 4 entries to 11 subsections covering STP,
  Yggdrasil/Ironwood, split-horizon, cryptographic identity (CJDNS,
  Tor, HIP), dual-layer encryption, Noise IK/XK/IKpsk2, index-based
  dispatch, transport-agnostic mesh, MMP measurement precedents
  (RTCP, Jacobson SRTT, QUIC spin bit, ETX, ECN), and Nostr primitives
- Add Metrics Measurement Protocol (MMP) section between routing and
  transport abstraction
- Fix Lightning Network Noise pattern: XK not IK
- Qualify transport observer claims (traffic patterns visible, FIPS
  identities not extractable from ciphertext)
- Add NAT traversal gap acknowledgment in transport section
- Standardize fd00::/8 notation (was fd::/8)
- Replace ambiguous "FIPS address" with explicit pubkey/node_addr/IPv6
  distinction
- Align identity section privacy qualifier with security section
- Separate Kleinberg and Thorup-Zwick attributions
- Merge redundant Protocol Architecture / Architecture Overview sections
- Add Sybil/zero-config tradeoff, eclipse attack, traffic analysis
  out-of-scope notes to security section
- Add key rotation tradeoff note to identity section
- Add bloom filter sizing future-analysis note
- Update External References with all new citations
2026-02-22 16:15:04 +00:00
Johnathan Corgan
0a72317b59 Design documentation illustration pass and FLP→FMP rename
Rename FIPS Link Protocol (FLP) to FIPS Mesh Protocol (FMP)

  The "Link Protocol" name understated the layer's scope — spanning tree
  construction, bloom filter routing, greedy forwarding, and mesh-wide
  coordination go well beyond link-level concerns. Rename fips-link-layer.md
  to fips-mesh-layer.md, update FLP→FMP throughout docs and source code
  (FLP_VERSION→FMP_VERSION, wire.rs, rx_loop.rs, spanning_tree.rs).

New SVG illustrations

  - Protocol stack: color-coded layer diagram replacing ASCII art
  - OSI mapping: side-by-side comparison with traditional networking layers
  - Bloom filter propagation: 6-node tree with sender-colored filter boxes
    showing split-horizon computation per link
  - Routing decision flowchart: 5-step priority chain with candidate ranking
    by tree distance and link performance
  - Coordinate discovery: sequence diagram showing LookupRequest propagation,
    response caching, and SessionSetup cache warming

Redesigned existing SVGs

  - Architecture overview: uniform node layout, U-shaped encrypted link
    connectors, separate end-to-end session line
  - Node architecture: split Router Core into FSP and FMP layers, reorganize
    transports into Overlay/Shared Medium/Point-to-Point categories
  - Identity derivation: wider boxes, visible encode arrow, dashed npub line

fips-intro.md revisions

  - Add inline references to prior work: Yggdrasil/Ironwood for coordinate
    routing, Noise Protocol Framework for IK handshakes, WireGuard for
    index-based session dispatch, Wikipedia for bloom filters, split-horizon,
    and greedy embedding
  - Add explanatory paragraphs after bloom filter diagram describing
    split-horizon filter computation and candidate selection behavior
  - Simplify transport abstraction language, remove I2P/LoRa references
  - Fix LookupRequest wording ("propagates" not "floods"), note intermediate
    node coordinate caching on lookup responses
  - Rewrite architecture overview prose to match redesigned diagrams
2026-02-21 22:05:44 +00:00
Johnathan Corgan
19efe06622 Add dest_coords to SessionAck for return-path routing
SessionAck previously only carried the responder's coordinates
(src_coords). When the return path diverged from the forward path
(e.g., after tree reconvergence), transit nodes on the return path
lacked the initiator's coordinates and couldn't route the SessionAck
back, causing handshake timeouts.

Add dest_coords (initiator's coordinates) to the SessionAck wire
format, mirroring SessionSetup's design. Transit nodes now cache both
endpoints' coordinates when forwarding a SessionAck, making the return
path self-sufficient regardless of path asymmetry.

Root cause confirmed by churn-20 sim log analysis: the n04-n14
handshake failure was caused by n15 (return-path transit) lacking
n04's coordinates, not by stale tree routes through a downed node.
2026-02-21 14:18:48 +00:00
Johnathan Corgan
cfb087a95d Update design docs for heartbeat, auto-reconnect, handshake retry, and sim improvements
fips-link-layer.md:
- Rewrite Liveness Detection: explicit Heartbeat (0x51) with 10s interval
  and 30s dead timeout replaces vague gossip-as-heartbeat description
- Add Auto-Reconnect section: MMP dead timeout triggers retry with
  unlimited backoff for auto_reconnect peers
- Add Handshake Message Retry section: link + session layer resend with
  exponential backoff within timeout window
- Add Heartbeat to Link Message Types table
- Update Implementation Status with three new implemented features

fips-configuration.md:
- Add handshake_resend_interval_ms, handshake_resend_backoff,
  handshake_max_resends to rate_limit table
- Add heartbeat_interval_secs, link_dead_timeout_secs to general table
- Add peers[].auto_reconnect to peers table
- Note auto-reconnect bypasses max_retries in retry section
- Update complete reference YAML with all new parameters

fips-wire-formats.md:
- Rename 0x51 from reserved Keepalive to implemented Heartbeat
- Update Disconnect reason 0x07 to Heartbeat liveness timeout

testing/chaos/README.md:
- Add runner.log to output files
- Add Directed Outbound Configs subsection
2026-02-21 13:10:02 +00:00