Commit Graph
92 Commits
Author SHA1 Message Date
Johnathan Corgan a1649ba4b7 Add dnsmasq split DNS and useful tools to Docker containers
Add dnsmasq to forward .fips queries to the FIPS daemon (port 5354)
and all other DNS to Docker's embedded resolver (127.0.0.11). Remove
the port 53 override from the node template so FIPS uses its default
port. Also add curl and python3 to the container image for testing.
2026-02-18 11:07:32 +00:00
Johnathan Corgan 71a0382a4a Add network impairment script, fix iperf loopback bug
Add netem.sh for simulating adverse network conditions (delay, loss,
jitter, duplication, reordering, corruption) on Docker test containers
using tc/netem. Includes three presets (lossy, congested, terrible) and
apply/remove/status actions.

Fix iperf-test.sh bug where all tests connected the client to its own
FIPS npub (loopback) instead of the remote server's npub, meaning
previous iperf results measured loopback performance rather than
cross-node throughput.

Document netem.sh in README.md.
2026-02-18 01:48:03 +00:00
Johnathan Corgan 06b5a623f9 Fix iperf-test.sh bandwidth extraction in non-live mode
The awk pattern used $(NF-2) which picked up the retransmit count (0)
instead of the bandwidth value, because the retransmit field sits
between the bandwidth unit and "sender". Search for the bits/sec field
by content instead of position.
2026-02-18 01:07:04 +00:00
Johnathan Corgan d46dc874ef Restructure design docs around protocol layers
Reorganize FIPS design documentation from implementation-centric
structure (routing, gossip protocol, wire protocol, transports) to
protocol-layer organization with clear service boundaries.

New documents (8):
- fips-transport-layer.md — transport layer spec
- fips-link-layer.md — FLP spec (peer auth, link encryption, forwarding)
- fips-session-layer.md — FSP spec (end-to-end encryption, sessions)
- fips-ipv6-adapter.md — IPv6 adaptation (TUN, DNS, MTU enforcement)
- fips-mesh-operation.md — routing, discovery, error recovery
- fips-wire-formats.md — consolidated wire format reference
- fips-spanning-tree.md — tree algorithm reference
- fips-bloom-filters.md — bloom filter math reference

Rewritten (2):
- fips-intro.md — breadth-first intro with layer model diagrams
- fips-software-architecture.md — slimmed to stable decisions

Updated (3):
- spanning-tree-dynamics.md — removed stale root refresh, aligned terminology
- fips-configuration.md — fixed priority type (u16 → u8)
- fips-state-machines.md — synced code examples with codebase

Deleted (6): fips-transports.md, fips-wire-protocol.md,
fips-gossip-protocol.md, fips-session-protocol.md, fips-routing.md,
fips-tun-driver.md (content absorbed into new structure)
2026-02-17 04:50:04 +00:00
Johnathan Corgan 3ca2f9500a Error recovery fixes and routing error rate limiting
- PathBroken handler: convert to async, trigger re-discovery via
  maybe_initiate_lookup(), reset COORDS_PRESENT warmup counter
  (was a stub that only invalidated coord_cache)

- CoordsRequired recovery timing: reset warmup counter in
  handle_lookup_response() when discovery completes for an
  established session, so COORDS_PRESENT packets fire after
  fresh coords are available (not just on CoordsRequired receipt)

- Routing error rate limiting: add RoutingErrorRateLimiter
  (100ms per-destination, matching ICMP PTB pattern) to gate
  send_routing_error() at transit nodes

- Remove root refresh dead code: the 1800s periodic root
  re-announcement in check_tree_state() only propagated to
  depth 1 (sequence-only changes don't cascade). Root loss
  detection relies on link failure propagation which works
  correctly.
2026-02-17 00:00:04 +00:00
Johnathan Corgan 8ca9db7480 Update configuration docs for cache merge, session parameters
Sync fips-configuration.md with code changes from sessions 101-102:

- Replace route_size with identity_size (cache merge)
- Update cache section description (single cache, not dual)
- Add idle_timeout_secs and coords_warmup_packets to session table
- Add both to Complete Reference YAML
- Fix UDP MTU default in Complete Reference (1197 → 1280)
2026-02-16 23:35:00 +00:00
Johnathan Corgan 1f60fbd7a2 COORDS_PRESENT warmup-then-reactive for DataPackets
Include source and destination coordinates on the first N DataPackets
of each session (default 5, configurable via
node.session.coords_warmup_packets). This warms transit node
coord_caches so multi-hop forwarding and error signal routing work
after SessionSetup cache entries expire.

On CoordsRequired receipt, reset the counter to re-enable coordinate
inclusion for the next N packets, handling mid-session cache expiry
and path changes.

Changes:
- SessionConfig: add coords_warmup_packets field (default 5)
- SessionEntry: add coords_warmup_remaining counter, initialized on
  Established transition (both initiator and responder paths)
- send_session_data(): attach coords via DataPacket::with_coords()
  while counter > 0, decrement per send
- handle_coords_required(): reset counter for affected session
- 4 new unit tests for counter lifecycle and config default
2026-02-16 23:28:24 +00:00
Johnathan Corgan f374370e5c Cache architecture: identity fix, cache merge, parent-change flush
Identity cache: remove TTL-based expiry (60s TTL broke active sessions
after expiry since handle_tun_outbound checks identity_cache before
session table). Replace with LRU-only eviction bounded by configurable
identity_size (default 10K). Lookup now touches timestamp for LRU
freshness.

Cache merge: unify coord_cache and route_cache into single coordinate
cache. Both stored NodeAddr→TreeCoordinate; the layer distinction was
conceptual, not functional. Discovery-sourced entries now get the same
TTL+refresh treatment as session-sourced entries. Simplifies
find_next_hop() to single cache lookup.

Parent-change flush: clear coord_cache after recompute_coords() in both
parent-switch paths of handle_tree_announce(). Stale coordinates after
tree reconvergence cause dead-end routing that's more expensive than
re-discovery.

Tested: 493 unit tests passed, clippy clean, Docker mesh 20/20,
Docker chain 6/6.
2026-02-16 22:56:34 +00:00
Johnathan Corgan 5f1c6c2c7c Add session idle timeout (90s) and identity cache expiry (60s)
Sessions in the Established state that have no activity for 90 seconds
are now automatically removed. This ensures idle sessions are torn down
before transit node coord_cache entries expire (300s TTL), so that when
traffic resumes a fresh SessionSetup re-warms transit node caches with
current coordinates.

The identity cache now stores registration timestamps and expires
entries after 60 seconds via lazy expiry on lookup. This prevents
unbounded growth while allowing natural repopulation through DNS
resolution on next use.

Timer ordering: identity (60s) < session (90s) < coord_cache (300s).

Both timeouts are configurable: node.session.idle_timeout_secs and
node.cache.identity_ttl_secs. Setting idle_timeout_secs to 0 disables
session idle purging.

Changes:
- Add idle_timeout_secs (default 90) to SessionConfig
- Add identity_ttl_secs (default 60) to CacheConfig
- Add timestamp to identity_cache entries, lazy expiry on lookup
- Add purge_idle_sessions() called from tick loop
- Remove #[cfg(test)] from SessionEntry::last_activity()
- 7 new tests covering timeout behavior and edge cases
2026-02-16 13:13:05 +00:00
Johnathan Corgan 5987cbfb69 Fix transit node coord_cache expiry breaking multi-hop routing
Transit nodes cache destination coordinates when they forward
SessionSetup messages (via try_warm_coord_cache). These coord_cache
entries have a 5-minute TTL, after which they expire. Once expired,
the transit node can no longer forward data packets for that
destination — find_next_hop returns None and the node sends
CoordsRequired errors back to the source. This creates a permanent
routing failure for any multi-hop path after 5 minutes of the initial
session establishment, even if traffic is actively flowing.

The root cause was that find_next_hop used coord_cache.get(), a
read-only lookup that checks expiry but never extends it. Active
forwarding did not keep the cache warm. Meanwhile, get_and_touch()
existed but only updated last_used without extending expires_at.

Fix:
- find_next_hop now calls coord_cache.get_and_touch() instead of get()
- get_and_touch now calls entry.refresh() instead of entry.touch(),
  which extends expires_at by the default TTL on each access
- find_next_hop signature changed from &self to &mut self to allow
  the mutable cache access

This ensures that as long as traffic flows through a transit node,
the coord_cache entries stay warm and routing continues to work.
Entries still expire after 5 minutes of inactivity as designed.
2026-02-16 12:41:45 +00:00
Johnathan Corgan 930f139787 Split cache.rs and config.rs into directory modules, create utils/
Module reorganization for three large single-file modules:

cache.rs (792 lines) split into cache/ directory:
- cache/mod.rs: CacheError, CacheStats, re-exports
- cache/entry.rs: CacheEntry with TTL/LRU tracking
- cache/coord_cache.rs: CoordCache (address-to-coordinate mappings)
- cache/route_cache.rs: RouteCache + CachedCoords (discovery routes)
- Added 22 new tests filling coverage gaps across all submodules

config.rs (1318 lines) split into config/ directory:
- config/mod.rs: ConfigError, IdentityConfig, Config struct with file
  loading/merge logic, all 24 integration tests
- config/node.rs: NodeConfig + 9 subsection structs (Limits, RateLimit,
  Retry, Cache, Discovery, Tree, Bloom, Session, Buffers)
- config/transport.rs: TransportInstances<T>, TransportsConfig, UdpConfig
- config/peer.rs: ConnectPolicy, PeerAddress, PeerConfig

DnsConfig and TunConfig moved to upper/config.rs to co-locate with the
upper layer components they configure (TUN interface, DNS responder).

index.rs moved to utils/index.rs as cross-cutting infrastructure that
serves both node and peer layers.
2026-02-15 17:56:32 +00:00
Johnathan Corgan d71e48b0f2 Module reorganization, identity test coverage, design doc corrections
Module reorganization:

- Split identity.rs (930 lines) into identity/ directory module:
  mod.rs, node_addr.rs, address.rs, peer.rs, local.rs, auth.rs,
  encoding.rs, tests.rs — following established bloom/, tree/, noise/
  pattern

- Group TUN, DNS, and ICMPv6 into upper/ module as the IPv6 adaptation
  layer: move tun.rs, icmp.rs, node/dns.rs into upper/

Identity test coverage (28 new tests, 52 total):

- Encoding error paths: invalid npub/nsec length, bad hex input
- NodeAddr: Debug, Display, as_slice, AsRef, Hash
- FipsAddress: from_slice, From trait, Debug, Display, Eq+Hash
- PeerIdentity: from_pubkey_full, pubkey_full parity paths, Debug
- Identity: keypair, pubkey_full, Debug
- AuthChallenge: from_bytes

Design doc corrections (fips-software-architecture.md):

- Identity struct: npub+nsec fields → keypair: Keypair with accessors
- Node struct: TunInterface → TunState, Transport → TransportHandle,
  Peer → PeerSlot
- Peer section: monolithic Peer → two-phase PeerSlot (PeerConnection +
  ActivePeer) with HandshakeState/ConnectivityState
- ActivePeer: npub → identity: PeerIdentity, ancestry Vec → Option,
  declaration/inbound_filter wrapped in Option
- BloomState: add 4 missing fields, fix update_debounce type
- DiscoveredPeer: field name and type corrections
2026-02-15 17:11:58 +00:00
Johnathan Corgan af4583d989 Bloom module test coverage, benchmarks, and design doc corrections
Testing:
- Add 14 bloom module tests (39 total): from_bytes error paths,
  from_slice round-trip, insert_bytes/contains_bytes, estimated_count
  saturation, Default/Debug traits, mark_changed_peers cascade
  prevention (4 scenarios), remove_peer_state, record_sent_filter,
  leaf_dependents accessor.

Benchmarks:
- Add criterion benchmark suite for bloom filter hot-path operations:
  insert, contains, merge, from_bytes, fill_ratio, estimated_count,
  equality, compute_outgoing_filter, mark_changed_peers, base_filter.
  Parameterized over realistic occupancy levels and peer counts.

Design doc corrections:
- Fix visited bloom filter hash_count in gossip protocol doc (7→5,
  matching code for 256-byte filter occupancy).
- Correct LookupResponse proof signature scope in fips-routing.md
  and fips-gossip-protocol.md: proof covers (request_id || target)
  only — coords excluded to survive tree reconvergence during lookup
  RTT.
2026-02-15 16:05:59 +00:00
Johnathan Corgan b8a1f322c2 Module reorganization and clippy cleanup
Move single-consumer modules into node/:
- rate_limit.rs, wire.rs, dns.rs — exclusively used by node subsystem
- Reduces top-level lib.rs from 16 to 13 modules

Split large files into focused subdirectories:
- noise.rs (1475 lines) → noise/{mod, handshake, session, replay, tests}.rs
- tree.rs (1479 lines) → tree/{mod, coordinate, declaration, state, tests}.rs
- bloom.rs (849 lines) → bloom/{mod, filter, state, tests}.rs
- All public APIs re-exported from mod.rs, no external import changes

Remove unused rate_limit defaults:
- HANDSHAKE_TIMEOUT_SECS, MAX_PENDING_INBOUND constants
- Default constructor eliminated in favor of with_params() taking config values

Fix all clippy warnings across codebase:
- Remove .clone() on Copy types, collapse nested ifs, replace match-return-None
  with ?, remove/gate unused code, fix loop indexing, remove unnecessary casts
- Box large PeerSlot enum variants to reduce size disparity
- cargo clippy --all-targets now reports zero warnings
2026-02-15 15:07:42 +00:00
Johnathan Corgan 89bc9cc4b0 Fix bloom filter cascade: gate re-announcements on content change
FilterAnnounce messages never settled in steady state because
handle_filter_announce unconditionally marked all peers for
re-announcement on every inbound filter, creating a perpetual
ping-pong at ~1 message/sec.

Added last_sent_filters tracking to BloomState. mark_changed_peers()
computes outgoing filters and compares against what was last sent,
only marking peers whose filter content actually differs.
2026-02-14 23:36:39 +00:00
Johnathan Corgan 57b2eef995 Configuration design doc: multi-file loading, full parameter reference
Documents cascading config search paths, CLI option, all 27 tunable
node parameters with types and defaults, minimal and complete YAML
examples.
2026-02-14 21:40:17 +00:00
Johnathan Corgan 7463d8799a Promote 27 hardcoded constants to configurable parameters
Add 9 config subsection structs (LimitsConfig, RateLimitConfig,
RetryConfig, CacheConfig, DiscoveryConfig, TreeConfig, BloomConfig,
SessionConfig, BuffersConfig) under node.* with serde defaults.

Wire all configurable values through to consuming code:
- Resource limits (max_connections, max_peers, max_links, max_pending_inbound)
- Rate limiting (handshake_burst, handshake_rate, handshake_timeout_secs)
- Retry/backoff (consolidate max_retries, base_interval_secs under
  node.retry.*, add max_backoff_secs)
- Cache sizes/TTL (coord_size, coord_ttl_secs, route_size)
- Discovery (ttl, timeout_secs, recent_expiry_secs)
- Spanning tree (root_refresh_secs, announce_min_interval_ms,
  parent_switch_threshold)
- Bloom filter (update_debounce_ms)
- Session/data plane (default_hop_limit, pending_packets_per_dest,
  pending_max_destinations)
- Internal buffers (packet_channel, tun_channel, dns_channel)
- Network internals (base_rtt_ms, tick_interval_secs)
- DNS responder TTL (dns.ttl)

REPLAY_WINDOW_SIZE kept as compile-time constant (array sizing).
Disable flaky test_discovery_100_nodes (run with --ignored).
2026-02-14 21:33:38 +00:00
Johnathan Corgan 20467f5650 Design doc audit: correct 7 code/doc divergences across 5 documents
Systematic review identified 12 divergences between design docs and
implementation. Corrected 7, deferred 3 for further analysis.

Changes:
- Session tie-breaker: npub → node_addr ordering
- Dual cache architecture: CoordCache (50K, TTL 300s) and RouteCache
  (10K, LRU) with correct names, sizes, and eviction policies
- LookupResponse routing: greedy-only → find_next_hop + reverse-path
- Discovery TTL default: 8 → 64
- Parent selection: v1 depth-only algorithm, cost metrics marked v2
- Leaf-only mode: implementation status note added
- Data overhead: 36-byte → 38-byte header
- Verification pass fixed stale cache/header refs in session protocol doc
2026-02-14 19:26:25 +00:00
Johnathan Corgan 5236fd02bf Replace two-node-udp with Docker network test harness, fix dead code warnings
Replace examples/two-node-udp/ (netns-based) with examples/docker-network/
(Docker compose, 5 nodes, mesh + chain topologies). The new harness uses
auto-detecting build script and includes SVG topology diagrams.

Remove unused get_session_mut, remote_addr(), remote_pubkey() methods.
Gate test-only accessors with #[cfg(test)]. Zero warnings in release build.
2026-02-14 18:38:26 +00:00
Johnathan Corgan 801a5ab222 Integrate discovery protocol into data plane for multi-hop routing
Wire the discovery protocol into the data plane path so that
find_next_hop() consults route_cache as a fallback when coord_cache
has no entry. When session initiation fails due to missing routes,
trigger discovery (initiate_lookup) instead of immediately sending
ICMPv6 Destination Unreachable. On discovery completion, retry
session initiation for any pending TUN packets.

Changes:
- find_next_hop() falls back to route_cache when coord_cache misses
- handle_tun_outbound() triggers discovery on session failure
- handle_lookup_response() retries session after discovery completes
- handle_coords_required() triggers discovery for missing coordinates
- Add pending_lookups deduplication with 10-second timeout
- Periodic cleanup of stale lookups in tick handler
- New test: route_cache fallback verification in find_next_hop
2026-02-14 16:59:40 +00:00
Johnathan Corgan 5fda3f64dd SVG network diagram for two-node UDP example
Replace ASCII art diagram in examples/two-node-udp/README.md with
two-node-udp.svg showing namespaces, veth pair, TUN devices, DNS
responders, and transport/session layers.
2026-02-13 16:00:58 +00:00
Johnathan Corgan fbdfe4e4f6 Identity derivation SVG diagram for fips-intro.md
Add fips-identity-derivation.svg showing the derivation chain from
pubkey to npub, node_addr, and IPv6 address with protocol roles and
privacy boundary. Replaces the code block in Node Address Derivation
with the diagram and consolidated descriptive text.
2026-02-13 15:18:48 +00:00
Johnathan Corgan 73129f16d7 Rename design docs for clarity, reorganize node architecture diagram
- fips-architecture.md → fips-software-architecture.md with all
  cross-references updated (4 files)
- fips-transport-abstraction.svg → fips-node-architecture.svg, moved
  from Transport Abstraction section to Architecture Overview in
  fips-intro.md
- Added descriptive paragraph for node architecture diagram covering
  three-layer design (application interfaces, router core, transports)
2026-02-13 15:03:06 +00:00
Johnathan Corgan 28af84d2f2 SVG diagrams for fips-intro: architecture, mesh topology, transport abstraction
Replace three ASCII art diagrams in fips-intro.md with SVG images:
- Architecture overview: 5-node 4-hop path with layered node boxes
- Mesh topology: 8-node network with spanning tree highlighted
- Transport abstraction: full node stack with 10 transport plugins
2026-02-13 14:21:40 +00:00
Johnathan Corgan 4a882ab591 TUN fd00::/8 route fix, two-node example updated from live testing
Add fd00::/8 route to TUN configure_interface() via rtnetlink - without
this route the kernel had no path to FIPS addresses ("network unreachable").

Update two-node-udp README based on live namespace testing:
- Remove resolvectl steps (doesn't work inside namespaces)
- Remove ICMPv6 Echo Reply mention (kernel handles it natively)
- Add namespace DNS limitation note and kernel ping reply explanation
- Simplify from 8 to 7 steps
2026-02-13 12:30:46 +00:00
Johnathan Corgan 2a37e2716f DNS responder for .fips domain, two-node UDP example
Add DNS responder that resolves <npub>.fips queries to FipsAddress IPv6
addresses. Resolution is pure computation (npub → NodeAddr → IPv6) with
identity cache priming as a side effect, enabling subsequent TUN packet
routing to non-peer destinations.

- New dns.rs module: resolve_fips_query(), handle_dns_packet() using
  simple-dns crate, run_dns_responder() async UDP server loop
- DnsConfig in config.rs: enabled, bind_addr (127.0.0.1), port (5354)
- Fourth select! arm in RX event loop for DNS identity channel
- DNS task spawn/abort in node lifecycle
- 10 new tests (420 total)

Add examples/two-node-udp/ with standalone walkthrough for testing two
FIPS nodes in Linux network namespaces over a veth pair. Includes
network diagram, config files, DNS routing setup, and troubleshooting.
2026-02-13 11:35:24 +00:00
Johnathan Corgan 7776c0f4ca Data plane integration: TUN outbound → session encryption → mesh delivery
Wire the TUN reader to the session layer for end-to-end IPv6 packet
delivery through the mesh. TUN reader forwards FIPS-destined packets
(fd::/8) to Node via tokio::sync::mpsc channel. Identity cache maps
FipsAddress prefix bytes to NodeAddr + PublicKey for reverse address
resolution, populated at peer promotion and session creation. Outbound
handler validates IPv6, looks up destination identity, routes through
established session or initiates new one with pending packet queue
(16/dest, 256 dests max). Queue flushed on session establishment.
ICMPv6 Destination Unreachable for unknown destinations. 6 new tests
including 3-node forwarded TUN data and pending queue flush. 410 tests
pass, clean build.
2026-02-13 04:19:30 +00:00
Johnathan Corgan 51597b6a5a End-to-end session establishment with 100-node bidirectional data test
Implement Noise IK session handshake between arbitrary endpoints, carried
inside SessionDatagram envelopes through the mesh. Sessions use a three-state
machine (Initiating → Established for initiator, Responding → Established
for responder on first DataPacket). Includes session initiation API,
encrypted data transfer, simultaneous initiation tie-break, error signal
handlers (CoordsRequired, PathBroken), and local delivery wiring in the
forwarding handler.

New files: node/session.rs (state types), handlers/session.rs (~500 lines,
all session message handlers + send path), tests/session.rs (11 tests).

100-node integration test establishes sessions across random topology,
sends bidirectional encrypted datagrams through injected TUN channels,
and verifies 200/200 deliveries with 100% session establishment. Reports
routing statistics including avg 4.1 link hops per datagram.

404 tests pass (up from 393).
2026-02-13 03:13:21 +00:00
Johnathan Corgan 85ca8a9ae4 100-node discovery integration test with per-node batched lookups
Add test_discovery_100_nodes: 100-node random topology (250 edges),
990 lookups (each node resolves every 10th peer), per-source-node
batching with quiescence detection. Validates 100% lookup resolution
via route_cache verification. 393 tests pass.
2026-02-12 18:15:34 +00:00
Johnathan Corgan 9a7fa921ab Discovery protocol: LookupRequest/LookupResponse handlers
Implement the coordinate discovery protocol with flood-based lookup
and reverse-path response routing.

Wire format: LookupRequest (0x30) encode/decode with TTL, visited
bloom filter, origin coords. LookupResponse (0x31) encode/decode
with target coords and Schnorr proof signature.

Handler logic: request dedup by request_id, visited filter loop
prevention, TTL enforcement, lazy purge of expired entries (10s).
Response routing: originator caches route in route_cache, transit
nodes reverse-path forward via recent_requests.

Node state: RecentRequest struct, route_cache (RouteCache), and
recent_requests map for dedup + reverse-path forwarding.

13 handler tests (9 unit + 4 integration) plus 4 protocol tests.
392 tests pass, clean build.
2026-02-12 14:46:33 +00:00
Johnathan Corgan 009101ee4a SessionDatagram forwarding handler with coordinate cache warming
Add handle_session_datagram handler replacing the 0x40 dispatch stub.
Transit nodes now decode the datagram envelope, enforce hop limits,
warm coordinate caches from SessionSetup/SessionAck/DataPacket payloads,
route via find_next_hop, and generate CoordsRequired/PathBroken error
signals on routing failure.

14 new tests covering decode errors, hop limit enforcement, local
delivery, cache warming for all session message types, single-hop and
multi-hop forwarding through live node chains, error signal generation,
and cache warming enabling subsequent routing. 375 tests pass.
2026-02-12 13:39:34 +00:00
Johnathan Corgan 62ce267677 Session message encode/decode, disconnect and routing integration tests
Add encode/decode for all session-layer message types (SessionSetup,
SessionAck, DataPacket, CoordsRequired, PathBroken) and SessionDatagram
link-layer envelope. Wire format follows design doc §8.0-8.6 with
address-only coordinate serialization (16 bytes/entry).

Struct additions: handshake_payload on SessionSetup/SessionAck, flags
on SessionAck, optional src_coords/dest_coords on DataPacket for route
cache warming (COORDS_PRESENT flag).

New disconnect integration tests verify cascading cleanup in multi-node
networks: chain peer removal, star hub departure, chain partition with
bloom filter update verification, all reason codes.

New routing tests validate bloom-filter-only transit behavior and peer
removal effects: confirm transit nodes without cached coords return None
(loop prevention), routing stops after partition, source-only coords
insufficient for multi-hop delivery.

361 tests pass (up from 335), zero warnings.
2026-02-12 13:01:29 +00:00
Johnathan Corgan d41009b778 SessionDatagram redesign: add src_addr, reclassify error signals
Add src_addr to SessionDatagram envelope (34-byte header: msg_type +
src_addr + dest_addr + hop_limit) so transit routers can route error
signals back to the packet's originator.

Reclassify CoordsRequired/PathBroken as link-layer error signals
(plaintext inside SessionDatagram) rather than e2e encrypted session
messages. Transit routers generate these when forwarding fails and
route them to src_addr; if source is also unreachable, drop silently.

Remove redundant src_addr/dest_addr/hop_limit from SessionSetup,
SessionAck, and DataPacket (now in envelope). DataPacket header
reduced from 36 to 4 bytes. Remove PathBroken.original_src.

Fix routing loop vulnerability: gate bloom filter path on having
cached dest_coords to prevent blind forwarding between peers.
Simplify select_best_candidate() to require coordinates.

Fix gossip protocol type codes (0x11->0x20, 0x12->0x30, 0x13->0x31)
for consistency across all design docs.

All 5 design docs updated and cross-checked for consistency.
335 tests pass, zero warnings.
2026-02-12 11:32:45 +00:00
Johnathan Corgan 2f8e97c0ab Implement greedy routing with bloom filter priority
Add the full next-hop routing algorithm to Node::find_next_hop():
- Local delivery, direct peer, bloom filter candidates, greedy tree
  routing fallback, with (link_cost, tree_distance, node_addr) ordering
- select_best_candidate() scores by peer→dest distance (not us→peer)
  with self-distance check to prevent routing loops
- TreeState::find_next_hop() for greedy tree routing with progress
  guarantee
- ActivePeer::link_cost() placeholder (constant 1.0) for future link
  quality metrics

Add routing tests including 100-node all-pairs reachability simulation
(9900/9900 delivered, 0 loops, avg 4.0 hops, max 8).

Update fips-routing.md to reflect bloom filter routing as the primary
forwarding mechanism, with greedy tree routing as fallback during
convergence windows.
2026-02-11 16:55:14 +00:00
Johnathan Corgan 066865ddd7 Split protocol.rs into protocol/ directory, standardize imports, clean lib.rs
- Replace use super::*; in node/lifecycle.rs with explicit imports,
  remove 10 resulting unused imports from node/mod.rs
- Split protocol.rs (1838 lines) into protocol/ directory:
  mod.rs (re-exports), error.rs, link.rs, tree.rs, filter.rs,
  discovery.rs, session.rs — each with co-located tests
- Remove unused flat re-exports from lib.rs for internal utility
  modules (wire, index, rate_limit, icmp, noise, tun)
- 316 tests pass, zero warnings
2026-02-11 05:02:02 +00:00
Johnathan Corgan cc29c51cac Refactor node/handlers.rs and node/tests.rs into subdirectories
Split handlers.rs (986 lines) into handlers/ with 5 subfiles organized
by responsibility: rx_loop, encrypted, handshake, dispatch, timeout.

Split tests.rs (2350 lines) into tests/ with 4 subfiles: unit tests,
handshake integration, spanning tree convergence, and bloom filter tests.
Shared test helpers extracted to tests/mod.rs.

Visibility adjusted from pub(super) to pub(in crate::node) for handler
methods now two levels deep. Unused imports cleaned up in node/mod.rs.

All 316 tests pass, zero warnings.
2026-02-11 03:49:45 +00:00
Johnathan Corgan 5d7af5b478 Implement FilterAnnounce send/receive, remove TTL/K-hop scoping
Add bloom filter reachability announcement protocol:
- FilterAnnounce encode/decode (wire format 0x20, 1035 bytes)
- node/bloom.rs: send/receive with debounce, split-horizon loop prevention
- Handler wiring: dispatch, tick, peer promotion/removal, cross-connection
- Five integration tests: 10-node, star, chain, ring, 100-node convergence

Remove TTL/K-hop mechanism from code and design docs after discovering
that per-entry TTL scoping is fundamentally incompatible with flat bloom
filter merge + regeneration architecture. Each node re-originates filters
with fresh TTL, making propagation unbounded regardless of TTL value.
Split-horizon remains the primary loop prevention mechanism.

Document spanning tree known limitations (v1) in spanning-tree-dynamics.md.

316 tests pass, clean build, zero warnings.
2026-02-11 03:16:25 +00:00
Johnathan Corgan 7bc5b21c3a Fix cross-connection session alignment for simultaneous auto-connect
When both nodes simultaneously initiate connections, each promotes its
inbound handshake first, leaving both sides with mismatched Noise sessions
and session indices. Resolve this in handle_msg2 using the existing
tie-breaker (smaller node_addr's outbound wins):

- Winner (smaller node): swaps ActivePeer to outbound session + indices
  via new replace_session() method
- Loser (larger node): keeps inbound session and original their_index
  (already references winner's outbound our_index from msg1)

Key insight: the loser must NOT update their_index from msg2, because the
msg2 sender_idx carries the winner's inbound index which becomes stale
after the winner swaps.

Defer outbound connection cleanup from promote_connection() to handle_msg2()
so outbound session state is available for the swap.

Verified with live two-node test over UDP in network namespaces: both nodes
connect, exchange TreeAnnounce, and converge to spanning tree within ~2s.
2026-02-11 01:41:21 +00:00
Johnathan Corgan 74eaeab3d4 Add spanning tree integration tests, fix parent ancestry propagation bug
Multi-node integration test infrastructure with 100-node random topology
convergence test (5 topology variants). Two-phase drain loop processes
Noise IK handshakes and TreeAnnounce cascades over live UDP until
quiescence.

Fix parent ancestry propagation in handle_tree_announce: when a node's
current parent updates its tree state (e.g., learning a new root from
upstream), coordinates must be recomputed even though the parent itself
didn't change. Without this, chains longer than 2 hops fail to converge.
2026-02-11 00:09:11 +00:00
Johnathan Corgan 1c2cda480d Implement spanning tree announcement send/receive protocol
Add TreeAnnounce v1 wire format with versioned encoding (version byte
0x01), slim ancestry entries (32 bytes each, no per-entry signatures),
and transitive trust model where only the direct peer's declaration
signature is verified.

Key changes:
- Enrich TreeCoordinate with CoordEntry metadata (sequence, timestamp)
- TreeAnnounce encode/decode with roundtrip tests
- Per-peer rate limiting (500ms minimum interval) on ActivePeer
- Parent selection: depth-based algorithm with broken-path detection
- New node/tree.rs module: send/receive, periodic refresh, cleanup
- Wire up dispatch, initial announce on promotion, tick integration
- Update gossip protocol design doc with trust model (§2.7)

304 tests pass, clean build.
2026-02-10 23:35:09 +00:00
Johnathan Corgan 9d36977ec8 Fix pending cross-connection cleanup, add auto-retry with exponential backoff
Fix promote_connection() to detect and clean up pending outbound
handshakes to the same peer, not just already-promoted peers. Previously,
when an inbound handshake completed while an outbound was still pending,
the outbound would linger until the 30s timeout.

Add auto-retry for failed outbound connections to auto-connect peers:
- New RetryState struct and node/retry.rs module
- Exponential backoff (default 5s base, max 5 attempts)
- Config: node.max_retries, node.base_retry_interval_secs
- check_timeouts() schedules retries, rx loop processes them
- promote_connection() clears retry state on success
- Remove unused PeerConnection retry fields (state now at Node level)

Move cleanup_stale_connection() logging to callers for context-appropriate
messages.

289 tests pass, clean build.
2026-02-10 22:25:31 +00:00
Johnathan Corgan 4445c46066 Fix secp256k1 parity in Noise IK, add disconnect protocol, cross-connection handling, timeout cleanup
Noise IK parity fix:
- Pre-message hash normalizes responder static key to even parity (0x02)
  so initiator and responder hash chains match regardless of actual parity
- ECDH uses shared_secret_point() + SHA-256(x-only) instead of
  SharedSecret::new() which includes a parity-dependent version byte
- Fixes handshake failure for ~50% of keys when initiator has only npub

Graceful disconnect protocol (link message 0x50):
- DisconnectReason enum with 8 reason codes
- Disconnect struct with encode/decode
- send_encrypted_link_message() reusable helper
- handle_disconnect() with immediate peer removal
- send_disconnect_to_all_peers() called during Node::stop()

Cross-connection fix in handle_msg1():
- addr_to_link check now distinguishes inbound duplicates (reject) from
  outbound links (cross-connection, allow and resolve via tie-breaker)
- remove_link() only clears addr_to_link if entry maps to same link_id
- Link cleanup and addr_to_link restoration in cross-connection branches

Handshake timeout cleanup:
- RX loop uses tokio::select! with 1-second interval tick
- check_timeouts() scans for stale (>30s) and failed connections
- cleanup_stale_connection() removes all associated state

Tests: 279 passing (4 new: cross-connection, stale cleanup, failed
cleanup, odd-parity handshake)
2026-02-10 21:25:26 +00:00
Johnathan Corgan e6f63678ba Refactor node.rs into node/ module directory
Split 2,597-line node.rs into four files under src/node/:
- mod.rs: types, struct definition, constructors, accessors, Debug impl
- lifecycle.rs: start(), stop(), peer connection initiation
- handlers.rs: RX loop, packet dispatch, message handlers, promote_connection
- tests.rs: full test suite (35 tests including integration tests)

No functional changes. All 267 tests pass, no new clippy warnings.
2026-02-10 19:05:05 +00:00
Johnathan Corgan 323bfa1ef7 Session 65: RX loop integration test and session layer doc correction
Add test_run_rx_loop_handshake exercising the full packet dispatch
path (UDP → channel → run_rx_loop → process_packet → handler) using
tokio::select! with timeout to release &mut self for assertions.

Correct session layer documentation: sessions are always established
between communicating nodes regardless of adjacency, not only for
non-adjacent traffic. Add adjacent-peer encryption flow example,
fix table description, rename Link Layer subsection to "Hop-by-Hop".

267 tests pass (266 existing + 1 new).
2026-02-10 18:45:41 +00:00
Johnathan Corgan cd7064595d Session 64: Complete peer authentication handshake and wire RX loop
Fix promote_connection to transfer NoiseSession, session indices,
transport ID, and source address to ActivePeer. Populate peers_by_index
during promotion for O(1) encrypted frame dispatch.

Add responder-side promotion in handle_msg1 — after sending msg2, the
responder immediately promotes to ActivePeer since Noise IK completes
in one step for the responder side.

Wire run_rx_loop into the fips binary using tokio::select with Ctrl+C
shutdown signal, so the daemon actually processes incoming packets.

Add end-to-end integration test: two nodes with real UDP sockets
perform full Noise IK handshake and bidirectional encrypted frame
exchange (266 tests, all passing).
2026-02-10 17:40:13 +00:00
Johnathan Corgan 2941705b95 Session 63: Routing protocol design review and limitations
- Document flood convergence limitation: visited bloom filter prevents
  loops but not convergent duplicates; request_id dedup elevated to
  protocol requirement in gossip protocol propagation rules
- Document capacity-blind greedy routing limitation with locally-measured
  link quality mitigation (no protocol-level cost claims to prevent
  adversarial traffic attraction)
- Add discovery path accumulation enhancement opportunity: signed per-hop
  entries enable source peer bias, router hints, and cache seeding
- Correct visited filter description in both routing and gossip docs
2026-02-06 15:26:03 +00:00
Johnathan Corgan 71f1db42e3 Truncate NodeAddr from 32 bytes to 16 bytes (first 128 bits of SHA-256)
128-bit truncated SHA-256 provides adequate collision resistance
(2^64 birthday bound, 2^128 pre-image) while cutting per-packet
header overhead nearly in half, aligning with Yggdrasil's approach
and IPv6 address size.

Code changes:
- NodeAddr inner type [u8; 32] → [u8; 16], from_pubkey takes first 16 bytes
- DATA_HEADER_SIZE 68 → 36, signing_bytes capacity 80 → 48
- proof_bytes capacity 40 → 24
- Updated all make_node_addr test helpers and size assertion tests

Doc changes:
- All wire format tables and offset calculations updated
- AncestryEntry size 112 → 96 bytes
- Packet size examples recalculated throughout
2026-02-06 14:32:15 +00:00
Johnathan Corgan dc12b4a82e Migrate session-layer addresses from FipsAddress to NodeAddr
Session packet headers use 32-byte NodeAddr (SHA-256 of pubkey) for
routing, not 16-byte FipsAddress (IPv6 ULA). Update all session-layer
structs (SessionDatagram, SessionSetup, SessionAck, DataPacket,
CoordsRequired, PathBroken) and CoordCache to use NodeAddr.

- DATA_HEADER_SIZE: 36 → 68 (two 32-byte addresses + 4 bytes overhead)
- CoordCache: HashMap key FipsAddress → NodeAddr
- Docs: fix header sizes in session-protocol and routing docs
- All 265 tests updated and passing
2026-02-06 13:48:36 +00:00
Johnathan Corgan 5e7342c57c Standardize naming conventions across docs and source
Rename NodeId to NodeAddr and npub to pubkey throughout documentation
and source code for consistency with the established identity model.
Add FIPS API vs IPv6 adapter overview to session protocol document.
Add transport address terminology note to wire protocol document.
2026-02-06 04:03:32 +00:00
Johnathan Corgan 7c8a5bd5ae Implement RX event loop with wire format dispatch
Wire format module (src/wire.rs):
- Discriminator-based packet framing (0x00/0x01/0x02)
- Header parsing: EncryptedHeader, Msg1Header, Msg2Header
- Serialization: build_msg1(), build_msg2(), build_encrypted()
- 11 unit tests for parsing and roundtrip

Session index tracking:
- PeerConnection: our_index, their_index, transport_id, source_addr
- ActivePeer: noise_session, indices, transport_id, current_addr
- Removed Clone from ActivePeer (NoiseSession nonce reuse risk)
- PromotionResult refactored to use NodeId instead of ActivePeer

Node RX event loop:
- run_rx_loop() with packet_rx channel consumption
- process_packet() discriminator dispatch
- handle_encrypted_frame() with O(1) index lookup
- handle_msg1() with rate limiting and inbound handshake
- handle_msg2() completing outbound handshakes
- dispatch_link_message() stub for link protocol

Infrastructure (from Session 56):
- IndexAllocator for random 32-bit session indices
- HandshakeRateLimiter with token bucket
- ReplayWindow for 2048-packet sliding window
- Bloom filter defaults updated to v1 spec

All 265 tests pass.
2026-02-02 17:20:06 +00:00
Johnathan Corgan ce6dba9225 Add privacy considerations section to routing document
Documents the tradeoff between metadata privacy and route error feedback:
- Intermediate routers see src_addr/dest_addr (traffic analysis possible)
- Source address needed for CoordsRequired/PathBroken error messages
- Deliberate choice: explicit feedback over silent drops + app timeouts
- Partial mitigation: addresses are SHA-256(npub), not npubs themselves
- Notes onion routing as alternative with different tradeoffs
2026-02-02 14:59:24 +00:00
Johnathan Corgan 7d5d1adedc Redesign bloom filter parameters with mathematical justification
Analysis revealed original parameters (4KB, k=7) were oversized:
- Expected filter occupancy ~250-800 entries, not ~4,096
- Original FPR estimates in docs were incorrect (5-7x optimistic)
- d^(2K) formula overcounted by assuming mesh vs tree structure

New v1 parameters:
- Filter size: 1 KB (was 4 KB) - 75% bandwidth reduction
- Hash functions: k=5 (was 7) - optimal for 800-1600 entries
- K-hop scope: 2 (unchanged)

Added forward compatibility via size_class field:
- Power-of-2 sizes (512B, 1KB, 2KB, 4KB) enable folding
- v1 requires size_class=1; future versions can negotiate larger
- Receivers can fold larger filters down to preferred size

Updated docs:
- fips-routing.md: Part 1 rewritten with math foundation
- fips-gossip-protocol.md: §3 and Appendix A.2 wire format
- fips-architecture.md: Configuration parameters
2026-02-02 14:41:28 +00:00
Johnathan Corgan 021bf69d73 Add detailed packet layout diagrams to protocol documentation
Add comprehensive wire format diagrams with byte offsets, field sizes,
and concrete examples to three protocol documents:

fips-wire-protocol.md (Appendix A):
- Encrypted frame (0x00) with plaintext structure breakdown
- Noise IK msg1/msg2 with payload breakdown
- Complete handshake flow diagram

fips-gossip-protocol.md (Appendix A):
- TreeAnnounce with ancestry entry structure and size table
- FilterAnnounce with bloom filter layout
- LookupRequest/LookupResponse with examples
- Message flow showing packet nesting

fips-session-protocol.md (§8):
- SessionSetup, SessionAck, DataPacket, CoordsRequired, PathBroken
- Full nested packet example showing link + session layers

fips-routing.md:
- Refactored Part 4 to "Route Cache Management"
- Moved wire format content to fips-session-protocol.md
2026-02-02 14:19:04 +00:00
Johnathan Corgan 7dc0b7fc52 Session 53: Route cache warming and session protocol refinements
Session protocol (fips-session-protocol.md):
- Clarified DNS entry point applies to IP-based apps; native FIPS uses npub
- Updated transport failover examples (UDP → WiFi, WiFi → LTE)
- Added roaming description to session independence section
- Expanded §5 with coords-on-demand mechanism for route cache recovery
- Added Noise IK vs XK privacy tradeoff note to §6

Routing (fips-routing.md):
- DataPacket now has optional coordinates (COORDS_PRESENT flag)
- Updated handle_data_packet to cache coords from packets
- Sender state machine: WARM/COLD based on CoordsRequired errors
- Updated packet type summary table

Architecture (fips-architecture.md):
- Fixed cross-references to session protocol sections
2026-02-02 03:39:13 +00:00
Johnathan Corgan a8115f7622 Unify Noise IK for both link and session layers
Design change: Session layer now uses Noise IK pattern (previously KK),
matching the link layer. Rationale: initiator knows destination npub,
responder learns initiator identity from handshake - same asymmetry as
link connections.

Document consistency fixes:
- Auth* events → Noise IK msg1/msg2 terminology in state machines
- PeerId → NodeId in routing code examples
- BloomUpdate → FilterAnnounce terminology
- Updated cross-references and tables
2026-02-02 02:25:39 +00:00
Johnathan Corgan e582f50de7 Session 51: Create fips-intro.md protocol introduction
New comprehensive protocol introduction replacing fips-design.md:
- What is FIPS / Why FIPS: goals and design philosophy
- How It Works: transport, spanning tree, bloom filters, routing overview
- Prior Work: Yggdrasil/Ironwood, Noise Protocol, WireGuard references
- Identity System: npub/nsec, node_id derivation, fd00::/8 addressing
- Two-Layer Encryption: link layer (Noise IK), session layer (Noise KK)
- Spanning Tree Protocol: root election, parent selection, gossip limits
- Bloom Filter Routing: filter explanation, propagation, discovery
- Transport Abstraction: transport/link distinction, bridging, types
- Security: threat model, Sybil resistance, accurate metadata exposure

Updated cross-references in README.md and other design docs.
Deleted obsolete fips-design.md.
2026-02-01 20:51:01 +00:00
Johnathan Corgan 7c2e11c5de Session 50: Design document reorganization and gossip protocol
- Create fips-gossip-protocol.md with wire formats for TreeAnnounce,
  FilterAnnounce, LookupRequest/LookupResponse
- Refactor fips-routing.md to reference gossip protocol for wire formats
- Update fips-session-protocol.md: remove reconciliation section,
  condense peer connection section
- Update spanning-tree-dynamics.md with gossip protocol reference
- Reorganize README.md with suggested reading order
- Remove obsolete fips-architecture-review.md
2026-02-01 17:42:17 +00:00
Johnathan Corgan 5830f54039 Session 50: Design document cleanup and decision documentation
Wire Protocol (fips-wire-protocol.md):
- Renamed to "FIPS Wire Protocol and Transport Layer Management"
- Updated intro describing transport layer purpose
- Documented reconnection handling approach (Option C)
- Clarified UDP as expected majority for initial deployments

Session Protocol (fips-session-protocol.md):
- Renamed to "FIPS Session Protocol Flow"
- Documented decisions throughout (removed alternatives analysis)
- Corrected Noise patterns: Link=IK, Session=KK
- Consolidated section 4 with fips-routing.md reference
- Rewrote section 5 terminology with decisions
- Merged duplicate crypto primitives sections
- Added route determination and packet forwarding details
2026-02-01 17:22:44 +00:00
Johnathan Corgan 4890940bb7 Session 49: Wire protocol design with session indices
- Add fips-wire-protocol.md: comprehensive packet dispatch design
  - Wire format: discriminator byte + session indices for O(1) dispatch
  - WireGuard-style roaming: crypto authority, not address
  - Security: rate limiting, replay protection, state machine strictness
  - Transport considerations for UDP, TCP, Tor

- Rename fips-protocol-flow.md → fips-session-protocol.md

- Update fips-design.md wire format section
  - Replace TLV with discriminator + index format
  - Cross-reference fips-wire-protocol.md for details

- Update cross-references in all design docs
2026-02-01 16:28:16 +00:00
Johnathan Corgan 0076e9930c Session 48: Wire format design and logging improvements
Wire format:
- Add HandshakeMessageType enum (NoiseIKMsg1=0x01, NoiseIKMsg2=0x02)
- Define unified TLV framing for handshake and post-handshake messages
- Update fips-design.md and fips-protocol-flow.md with wire format details

Initialization:
- Defer TUN setup until after peer connections initiated
- Order: packet channel → transports → peers → TUN

Logging cleanup:
- Consolidate node/TUN info logs into aligned multi-line format
- TUN shutdown: single start/stop message, remove intermediate noise
- Remove ip link show callout and redundant Starting node message
- Change UDP transport stopped to debug level
2026-02-01 05:18:59 +00:00
Johnathan Corgan 8ba24c38ae Session 48: Initialization order and logging improvements
- Defer TUN setup until after peer connections initiated
- Split TUN packet/device logs into aligned multi-line format
- Remove ip link show debug callout from fips binary
2026-02-01 03:33:56 +00:00
Johnathan Corgan 58be9e2e59 Separate protocol layers: link vs session message types
Refactor protocol.rs to cleanly separate two protocol layers:

Link layer (hop-by-hop, peer-to-peer):
- LinkMessageType: TreeAnnounce, FilterAnnounce, LookupRequest,
  LookupResponse, SessionDatagram
- New SessionDatagram struct for encapsulating session payloads

Session layer (end-to-end):
- SessionMessageType: SessionSetup, SessionAck, DataPacket,
  CoordsRequired, PathBroken

Also:
- Remove obsolete Hello/Challenge/Auth/AuthAck types (replaced by Noise IK)
- Update all design docs for consistency with two-layer architecture
- Add "Link vs Session Encryption" documentation

All 219 tests pass.
2026-02-01 03:19:54 +00:00
Johnathan Corgan 9a6fa07e77 Session 46: Noise IK peer authentication implementation
Implement Noise Protocol Framework for peer authentication using the IK
pattern, which allows responders to learn initiator identity from the
encrypted handshake message.

Protocol: Noise_IK_secp256k1_ChaChaPoly_SHA256
- Message 1: 82 bytes (ephemeral + encrypted static)
- Message 2: 33 bytes (ephemeral only)

New noise.rs module (~600 lines):
- HandshakeState: Manages handshake for both initiator/responder roles
- NoiseSession: Post-handshake symmetric encryption
- CipherState: ChaCha20-Poly1305 with nonce counter
- SymmetricState: HKDF-SHA256 key derivation

PeerConnection integration:
- Simplified HandshakeState enum (Initial/SentMsg1/ReceivedMsg1/Complete/Failed)
- start_handshake(): Initiator generates msg1
- receive_handshake_init(): Responder processes msg1, discovers identity
- complete_handshake(): Initiator completes with msg2
- take_session(): Extract NoiseSession for ActivePeer

Identity helpers:
- Identity::keypair() for Noise operations
- PeerIdentity::from_pubkey_full() preserves parity for ECDH
- PeerIdentity::pubkey_full() returns full key or derives with even parity

Node changes:
- initiate_peer_connection() now starts handshake and sends msg1
- Method is async to use transport's async send

All 219 tests pass.
2026-02-01 01:28:01 +00:00
Johnathan Corgan 86c49dfb04 Implement PeerSlot architecture with two-phase peer lifecycle
Refactor peer management into two distinct phases:

PeerConnection (handshake phase):
- Indexed by LinkId (identity unknown for inbound connections)
- HandshakeState: AwaitingHello → SentHello → SentAuth → AwaitingAuthAck → Complete
- Tracks expected identity, retry count, timing

ActivePeer (authenticated phase):
- Indexed by NodeId (verified identity)
- ConnectivityState: Connected → Stale → Reconnecting → Disconnected
- Holds tree state, bloom filter, routing data

Cross-connection handling:
- Deterministic tie-breaker: smaller node_id's OUTBOUND wins
- PromotionResult enum for promotion outcomes
- Both nodes reach same conclusion independently

Node refactoring:
- Split storage: connections (by LinkId) + peers (by NodeId)
- Add addr_to_link reverse lookup for packet dispatch
- promote_connection() handles promotion with cross-connection detection
2026-02-01 00:50:47 +00:00
Johnathan Corgan a5a62b3768 Session 43: CLI config option and state machine design
Add command-line argument parsing with clap:
- -c/--config option to specify config file path
- Overrides default search path when provided
- Proper error handling for missing/invalid files

Improve logging:
- Peer connection log now uses separate log entries per field
- Better readability with aligned timestamps

Fix ICMPv6 error handling:
- Add multicast destination filter to should_send_icmp_error()
- Router Solicitation packets (ff02::2) now silently dropped
- Add test case for multicast destination

Add phase-based state machine design document:
- Document pattern where lifecycle phases use distinct structs in enum
- PeerSlot::Connecting(PeerConnection) -> PeerSlot::Active(ActivePeer)
- Benefits: type safety, memory efficiency, security
- Describes timeout handling and lookup table requirements
2026-01-31 23:33:02 +00:00
Johnathan Corgan d2851d8406 Add peer configuration and connection initiation
Config changes:
- Add ConnectPolicy enum (AutoConnect, OnDemand, Manual)
- Add PeerAddress struct (transport, addr, priority)
- Add PeerConfig struct (npub, alias, addresses, connect_policy)
- Add top-level 'peers' section to Config (after transports)
- Add accessor methods: peers(), auto_connect_peers()

Node changes:
- Add find_transport_for_type() to match transports by name
- Add initiate_peer_connections() called after transports start
- Add initiate_peer_connection() creates Link and Peer in Connecting state
- Node::start() now initiates connections to auto-connect peers

This completes the node startup sequence through step 5 (connect
to static peers). Peers are created in Connecting state; the auth
handshake implementation will transition them to Active.
2026-01-31 22:53:26 +00:00
Johnathan Corgan 738775258a Session 41: Peer authentication protocol design
Design 3-message peer auth handshake (AuthInit → AuthChallenge → AuthComplete):
- Deterministic crossing connection handling via npub ordering
- Domain-separated signatures (fips-peer-auth-v1)
- New message types: 0x09 AuthInit, 0x0a AuthChallenge, 0x0b AuthComplete

fips-design.md:
- Replace 4-message auth with 3-message design
- Add crossing connection handling rules
- Add message structures and signature construction

fips-architecture.md:
- Expand peer lifecycle state machine with auth states
- Add Node Startup Sequence section
- Add Static Peer Retry Policy (exponential backoff, jitter)
- Add Inbound Connection Acceptance section
- Update peer config for static-only initial impl

fips-protocol-flow.md:
- Add §7 Peer Connection Establishment
- Add §7.2 Post-Authentication flow

Design decisions: static peers only, always do peer auth regardless of
transport, accept all authenticated inbound connections.
2026-01-31 19:13:15 +00:00
Johnathan Corgan b54c347e76 Session 40: Reconcile design docs with protocol flow decisions
Align terminology and cross-references across all FIPS design documents
based on decisions made in fips-protocol-flow.md (Session 39).

Key changes:
- Distinguish "Routing Session" (hop-by-hop cache) from "Crypto Session"
  (end-to-end Noise KK encryption) throughout all docs
- Add handshake_payload field to SessionSetup/SessionAck for combined
  establishment of routing and crypto sessions
- Update fips-design.md encryption section to reference Noise KK
- Add SessionSetup (0x06), SessionAck (0x07), CoordsRequired (0x08)
  message types
- Add Crypto Session Management config section to fips-architecture.md
- Add cross-references to fips-protocol-flow.md in all design docs
- Update reconciliation tracking in protocol-flow.md §7
2026-01-31 18:28:14 +00:00
Johnathan Corgan 5d190885ec Session 39: Protocol traffic flow design document
Add comprehensive design doc for FIPS protocol message flow covering:

- DNS-based identity cache priming (npub.fips format)
- TUN reader processing pipeline
- Crypto session (Noise KK with secp256k1) vs routing session distinction
- Combined establishment model (routing + crypto in single round-trip)
- Noise KK pattern with ChaCha20-Poly1305, AEAD-only authentication
- Route discovery via bloom filters and LookupRequest flooding
- Crossing hellos handling with deterministic tie-breaker (lower npub wins)

Includes reconciliation section tracking alignment with existing design docs
(fips-routing.md terminology updates pending).
2026-01-31 18:09:38 +00:00
Johnathan Corgan a317896367 Add transport instance naming and reorder node startup
- Add name field to UdpTransport for named config instances
- Add name() and local_addr() accessors to TransportHandle
- Update UdpTransport logging to include instance name
- Remove redundant Node-level transport startup logging
- Reorder Node::start() to initialize transports before TUN
2026-01-31 04:16:11 +00:00
Johnathan Corgan 4bbecccc11 Session 37: Transport-Node lifecycle integration
- Added TransportHandle enum for polymorphic transport dispatch
- Node now owns transports via HashMap<TransportId, TransportHandle>
- Added packet channel fields (packet_tx, packet_rx) to Node
- Transport initialization in Node::start() with graceful degradation
- Transport shutdown in Node::stop() before TUN cleanup
- Factory method create_transports() instantiates from config

Configuration redesign:
- New transports section with TransportInstances<T> enum
- Single instance: config directly under transport type (no naming overhead)
- Named instances: nested under instance names
- #[serde(deny_unknown_fields)] ensures correct untagged enum matching
- Instance names are Option<&str> - None for single, Some(name) for named

Updated Node transport methods:
- transport_count(), get_transport(), get_transport_mut()
- transport_ids(), packet_rx()

All 189 tests pass (4 new config parsing tests).
2026-01-31 01:20:13 +00:00
Johnathan Corgan d30865f60b Add UDP transport implementation
- Convert transport.rs to module directory (transport/mod.rs + transport/udp.rs)
- Add packet channel types for transport→Node communication:
  - ReceivedPacket struct with transport_id, remote_addr, data, timestamp
  - PacketTx/PacketRx type aliases for tokio mpsc channels
  - packet_channel() constructor function
- Add UdpConfig to config.rs (enabled, bind_addr, mtu)
- Implement UdpTransport with async lifecycle:
  - start_async(): binds socket, spawns receive loop
  - stop_async(): aborts receive task, closes socket
  - send_async(): sends packet with MTU validation
  - discover(): returns empty (peer config is node-level)
- Update lib.rs with new re-exports
- Add tokio net and time features to Cargo.toml

All 185 tests pass.
2026-01-30 21:02:33 +00:00
Johnathan Corgan c421dad525 Spanning tree init and Node lifecycle refactoring
Spanning tree initialization:
- TreeState now uses sequence=1 and current timestamp per protocol spec
- Added ParentDeclaration::sign() and TreeState::sign_declaration() methods
- Node initialization logs identity (node_id, address)

Node lifecycle refactoring:
- Moved TUN init and thread spawning into async Node::start()
- Moved TUN shutdown and thread cleanup into async Node::stop()
- Node owns I/O infrastructure (thread handles, TunTx channel)
- Removed: init_tun(), take_tun_device(), tun_device(), tun_device_mut()
- Added: tun_tx() accessor for packet sending

tun.rs:
- Added run_tun_reader() function (moved from binary)

fips.rs binary reduced from 230 to 117 lines. All 171 tests pass.
2026-01-30 17:40:33 +00:00
Johnathan Corgan ca24ba02b7 Add TUN driver design document
- docs/design/fips-tun-driver.md: comprehensive TUN interface documentation
- Architecture diagrams, component descriptions, packet flow
- Implementation status checklist (completed vs planned)
- Testing instructions and configuration reference
2026-01-30 05:38:58 +00:00
Johnathan Corgan 385033fcb7 Add ICMPv6 Destination Unreachable and TUN reader/writer architecture
- New icmp.rs module: builds RFC 4443 compliant ICMPv6 Type 1 Code 0
  responses with proper checksum and packet validation
- TUN reader/writer separation: fd duplication allows independent I/O
  on separate threads
- TunWriter services mpsc queue for multiple future packet sources
- Reader now sends ICMPv6 errors for unroutable packets instead of
  silently dropping them
- 171 tests passing
2026-01-30 05:25:28 +00:00
Johnathan Corgan db8aa5825d Session 33: TUN interface lifecycle management
- Add TUN interface detection on startup, delete existing before create
- Add proper interface deletion on shutdown via netlink
- Add TUN packet reader in separate thread with DEBUG logging
- Add graceful shutdown handling for expected "Bad address" error
- Add take_tun_device() to Node for reader ownership transfer
- Add shutdown_tun_interface() public function for cleanup by name
- Add tokio signal feature for Ctrl+C handling
2026-01-30 04:57:35 +00:00
Johnathan Corgan bfdc532058 Remove --wait mode from fips binary
No longer needed since lldb-gdbserver-start.sh launches the binary
directly via gdbserver mode.
2026-01-30 03:43:05 +00:00
Johnathan Corgan 4f8107be83 Add --wait flag and ip link debug output
- Add --wait / -w command-line flag for debugger attachment
- When --wait is set, daemon blocks after init with thread::park()
- Add ip link show output after TUN device creation for debugging
2026-01-30 00:46:17 +00:00
Johnathan Corgan 6d95bdc979 Add TUN interface with async netlink configuration
- Implement fips.rs binary with config loading, node creation, and logging
- Create src/tun.rs module (TunDevice, TunState, TunError)
- Use rtnetlink crate for IPv6 address configuration
- Make TunDevice::create() and Node::init_tun() async
- Add IPv6 disabled check with helpful error message
- Add rtnetlink, tokio, futures dependencies
- Single tokio current_thread runtime for entire driver

162 tests pass.
2026-01-29 22:12:27 +00:00
Johnathan Corgan 185fe50570 Restructure config to match sysctl-style paths
- Add NodeConfig wrapper: node.identity.nsec, node.leaf_only
- YAML structure mirrors architecture documentation exactly
- Move main.rs to src/bin/fips.rs (empty binary placeholder)
- Add "Connection Model" section to fips-transports.md documenting
  connectionless vs connection-oriented transport categories
- Fix clippy warnings (dead_code, clone_on_copy, assertions_on_constants)
2026-01-29 19:49:37 +00:00
Johnathan Corgan 6e343c35e5 Implement FIPS foundational entity structures
Add 7 new modules with all core data types for the mesh routing protocol:
- tree.rs: ParentDeclaration, TreeCoordinate, TreeState
- bloom.rs: BloomFilter (4KB/7 hash), BloomState with debouncing
- transport.rs: TransportId, LinkId, Link, Transport trait, LinkStats
- protocol.rs: Auth messages, TreeAnnounce, FilterAnnounce, LookupRequest/Response, SessionSetup, DataPacket
- cache.rs: CoordCache, RouteCache with LRU eviction and TTL expiry
- peer.rs: Peer lifecycle states, filter tracking, UpstreamPeer
- node.rs: Node container with resource limits

All entities have constructors, error types, and comprehensive tests (161 total).
Stub methods with todo!() for behavior to be implemented later.
No state machine logic, protocol handlers, or async code yet.
2026-01-29 18:49:11 +00:00
Johnathan Corgan 3b6a4da17d Complete FIPS architecture review cleanup
- Rename fips-links.md to fips-transports.md, update all references
- Update README with all 6 design documents in organized sections
- Reorganize architecture review: remove verbose resolved items, consolidate
  deferred items, focus on actionable issues
- Clarify Transport/Link/Peer lifecycle in architecture doc:
  - Transports static after startup
  - Links on-demand, driven by peer lifecycle
  - Connectionless transports (UDP) immediate established
  - Connection-oriented (Tor) require link setup before auth
- Add Resource Limits configuration section (max_peers, max_transports,
  max_pending_auth, max_pending_lookups, memory_budget)
- Close timer management as non-issue (tokio handles scale)
- Defer init/shutdown to future iteration

Review status: 6 resolved, 5 deferred, 4 low-priority open
2026-01-29 18:17:07 +00:00
Johnathan Corgan fc20c47119 Resolve architecture review items and reconcile terminology
- Standardize coordinate ordering to [self, parent, ..., root] across all docs
- Fix 8 coordinate ordering instances in spanning-tree-dynamics.md
- Clarify cache naming: discovery.cache and session.cache configure Node.coord_cache
- Reconcile Transport vs Link terminology:
  - Transport = interface/medium (UDP socket, Ethernet NIC)
  - Link = connection instance to a specific peer
- Update fips-design.md: replace FipsLink trait with architecture reference
- Rename fips-links.md to "Transport Protocols", add terminology section
- Update review doc: mark items 1, 5, 13 RESOLVED; items 2-4, 7 DEFERRED
2026-01-29 17:00:33 +00:00
Johnathan Corgan c2ba0e3fcb Add FIPS software architecture document
Create comprehensive architecture document covering:
- Core entities: Node, Transport, Link, Peer hierarchy
- Transport vs Link distinction (interface vs connection)
- State machines for Transport, Link, and Peer lifecycles
- Reference transport types: UDP/IP, Ethernet, WiFi, Tor
- Spanning tree and Bloom filter state requirements
- Self-healing protocol design (no ack/retry needed)
- Leaf-only operation for constrained devices
- Comprehensive sysctl-style configuration reference

Also add architecture review document capturing identified
issues to resolve before implementation begins.
2026-01-29 16:36:46 +00:00
Johnathan Corgan dc0acf32ea Add FIPS routing design document
Create fips-routing.md covering the complete routing architecture:
- Bloom filter design: 4KB filters, K=2 scope, event-driven updates
- Discovery protocol: LookupRequest/Response with signed proofs
- Greedy tree routing using coordinates from discovery
- Session establishment model for minimal data packet overhead
- Router coordinate caching with LRU eviction

Key design decisions:
- Leaf-only mode for constrained devices (single peer handles routing)
- Separation of discovery (find destination) from routing (deliver packets)
- Session setup pays coordinate cost once; data packets carry only addresses
- 36-byte data packet header comparable to IPv6

Update design docs README to include new document.
2026-01-26 00:12:47 +00:00
Johnathan Corgan d8cba510a7 Add FIPS link protocol design document
New docs/design/fips-links.md covering:
- Link terminology (L2/L2-equivalent, not "transport")
- Link categories: overlay, shared medium, point-to-point
- Characteristics table: encapsulation, addressing, MTU, latency,
  reliability, bandwidth, discovery
- TCP-over-TCP problem and UDP preference rationale
- NAT/firewall traversal requirements
- IPv6 interface exposure to applications

Update README.md with new document entry.
2026-01-25 22:52:37 +00:00
Johnathan Corgan 7194200f55 Add docs/design directory with protocol specifications
Move design documents from working directory into source tree:
- fips-design.md - Full protocol design specification
- spanning-tree-dynamics.md - Spanning tree protocol dynamics study

Add README index files for docs/ and docs/design/.
2026-01-25 22:02:54 +00:00
Johnathan Corgan b80b3fbecf Add YAML configuration system and nsec encoding
Configuration system:
- New config module with cascading file search
- Priority: ./fips.yaml > ~/.config/fips/ > ~/.fips.yaml > /etc/fips/
- Identity section with optional nsec (bech32 or hex format)
- Generate new keypair if nsec not configured

Identity module additions:
- encode_nsec() and decode_nsec() for NIP-19 bech32 format
- decode_secret() accepts both nsec and hex formats
- Identity::from_secret_str() constructor

Dependencies: serde, serde_yaml, dirs, hex

41 tests passing (20 identity + 15 config + 6 nsec)
2026-01-25 01:08:02 +00:00
Johnathan Corgan ac2f6a75c8 Add npub encoding and PeerIdentity to identity module
- Add bech32 dependency for NIP-19 npub encoding
- Add encode_npub/decode_npub functions
- Add Identity::npub() method
- Add PeerIdentity type for remote peers (public key only)
  - from_npub() constructor
  - verify() for signature verification
- Export new types from lib.rs
- Update main.rs with verbose authentication demo
- 20 tests passing
2026-01-25 00:40:59 +00:00
Johnathan Corgan db4989a790 Add Ipv6Addr conversion for FipsAddress 2026-01-25 00:03:19 +00:00
Johnathan Corgan 5c0e84f239 Add identity module with NodeId, FipsAddress, and auth challenge
Implements FIPS Identity System (Section 1 of design doc):
- NodeId: 32-byte SHA-256 hash of npub, with Ord for root election
- FipsAddress: 128-bit IPv6-compatible address (0xfd prefix)
- Identity: keypair holder with sign/verify methods
- AuthChallenge/AuthResponse: challenge-response authentication

All 11 tests passing.
2026-01-24 23:57:48 +00:00
Johnathan Corgan 6323c219b7 Initial FIPS project 2026-01-24 23:37:28 +00:00