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
This commit is contained in:
Johnathan Corgan
2026-02-21 22:05:44 +00:00
parent 19efe06622
commit 0a72317b59
27 changed files with 997 additions and 502 deletions
+4 -4
View File
@@ -2,7 +2,7 @@
use crate::node::{Node, NodeError};
use crate::transport::ReceivedPacket;
use crate::node::wire::{CommonPrefix, PHASE_ESTABLISHED, PHASE_MSG1, PHASE_MSG2, FLP_VERSION, COMMON_PREFIX_SIZE};
use crate::node::wire::{CommonPrefix, PHASE_ESTABLISHED, PHASE_MSG1, PHASE_MSG2, FMP_VERSION, COMMON_PREFIX_SIZE};
use std::time::Duration;
use tracing::{debug, info};
@@ -110,11 +110,11 @@ impl Node {
None => return, // Malformed prefix
};
if prefix.version != FLP_VERSION {
if prefix.version != FMP_VERSION {
debug!(
version = prefix.version,
transport_id = %packet.transport_id,
"Unknown FLP version, dropping"
"Unknown FMP version, dropping"
);
return;
}
@@ -133,7 +133,7 @@ impl Node {
debug!(
phase = prefix.phase,
transport_id = %packet.transport_id,
"Unknown FLP phase, dropping"
"Unknown FMP phase, dropping"
);
}
}
+1 -1
View File
@@ -144,7 +144,7 @@ impl FspCommonPrefix {
/// ```
///
/// The full 12-byte header is used as AAD for the AEAD construction.
/// No receiver_idx — unlike FLP, FSP is end-to-end (dispatched by src_addr
/// No receiver_idx — unlike FMP, FSP is end-to-end (dispatched by src_addr
/// from the SessionDatagram envelope, not by index).
#[derive(Clone, Debug)]
pub struct FspEncryptedHeader {
+2 -2
View File
@@ -204,7 +204,7 @@ pub(super) fn print_tree_snapshot(label: &str, nodes: &[TestNode]) {
///
/// Returns the number of packets processed.
pub(super) async fn process_available_packets(nodes: &mut [TestNode]) -> usize {
use crate::node::wire::{CommonPrefix, FLP_VERSION, PHASE_ESTABLISHED, PHASE_MSG1, PHASE_MSG2, COMMON_PREFIX_SIZE};
use crate::node::wire::{CommonPrefix, FMP_VERSION, PHASE_ESTABLISHED, PHASE_MSG1, PHASE_MSG2, COMMON_PREFIX_SIZE};
let mut count = 0;
for node in nodes.iter_mut() {
@@ -213,7 +213,7 @@ pub(super) async fn process_available_packets(nodes: &mut [TestNode]) -> usize {
continue;
}
if let Some(prefix) = CommonPrefix::parse(&packet.data) {
if prefix.version != FLP_VERSION {
if prefix.version != FMP_VERSION {
continue;
}
match prefix.phase {
+10 -10
View File
@@ -1,6 +1,6 @@
//! Wire Format Parsing and Serialization
//!
//! Defines the FIPS link-layer wire format (FLP) for packet dispatch.
//! Defines the FIPS mesh-layer wire format (FMP) for packet dispatch.
//! All packets begin with a 4-byte common prefix followed by phase-specific fields.
//!
//! ## Common Prefix (4 bytes)
@@ -24,8 +24,8 @@ use crate::noise::{HANDSHAKE_MSG1_SIZE, HANDSHAKE_MSG2_SIZE, TAG_SIZE};
// Constants
// ============================================================================
/// FLP protocol version (4 high bits of byte 0).
pub const FLP_VERSION: u8 = 0;
/// FMP protocol version (4 high bits of byte 0).
pub const FMP_VERSION: u8 = 0;
/// Phase value for established (encrypted) frames.
pub const PHASE_ESTABLISHED: u8 = 0x0;
@@ -70,7 +70,7 @@ pub const FLAG_SP: u8 = 0x04;
// Common Prefix
// ============================================================================
/// Parsed common packet prefix (first 4 bytes of every FLP packet).
/// Parsed common packet prefix (first 4 bytes of every FMP packet).
///
/// Wire format:
/// ```text
@@ -156,7 +156,7 @@ impl EncryptedHeader {
let version = data[0] >> 4;
let phase = data[0] & 0x0F;
if version != FLP_VERSION || phase != PHASE_ESTABLISHED {
if version != FMP_VERSION || phase != PHASE_ESTABLISHED {
return None;
}
@@ -222,7 +222,7 @@ impl Msg1Header {
let version = data[0] >> 4;
let phase = data[0] & 0x0F;
if version != FLP_VERSION || phase != PHASE_MSG1 {
if version != FMP_VERSION || phase != PHASE_MSG1 {
return None;
}
@@ -278,7 +278,7 @@ impl Msg2Header {
let version = data[0] >> 4;
let phase = data[0] & 0x0F;
if version != FLP_VERSION || phase != PHASE_MSG2 {
if version != FMP_VERSION || phase != PHASE_MSG2 {
return None;
}
@@ -317,7 +317,7 @@ pub fn build_msg1(sender_idx: SessionIndex, noise_msg1: &[u8]) -> Vec<u8> {
let payload_len = (4 + noise_msg1.len()) as u16; // sender_idx + noise_msg1
let mut packet = Vec::with_capacity(MSG1_WIRE_SIZE);
packet.push(CommonPrefix::ver_phase_byte(FLP_VERSION, PHASE_MSG1));
packet.push(CommonPrefix::ver_phase_byte(FMP_VERSION, PHASE_MSG1));
packet.push(0x00); // flags must be zero
packet.extend_from_slice(&payload_len.to_le_bytes());
packet.extend_from_slice(&sender_idx.to_le_bytes());
@@ -334,7 +334,7 @@ pub fn build_msg2(sender_idx: SessionIndex, receiver_idx: SessionIndex, noise_ms
let payload_len = (4 + 4 + noise_msg2.len()) as u16; // sender + receiver + noise
let mut packet = Vec::with_capacity(MSG2_WIRE_SIZE);
packet.push(CommonPrefix::ver_phase_byte(FLP_VERSION, PHASE_MSG2));
packet.push(CommonPrefix::ver_phase_byte(FMP_VERSION, PHASE_MSG2));
packet.push(0x00); // flags must be zero
packet.extend_from_slice(&payload_len.to_le_bytes());
packet.extend_from_slice(&sender_idx.to_le_bytes());
@@ -353,7 +353,7 @@ pub fn build_established_header(
payload_len: u16,
) -> [u8; ESTABLISHED_HEADER_SIZE] {
let mut header = [0u8; ESTABLISHED_HEADER_SIZE];
header[0] = CommonPrefix::ver_phase_byte(FLP_VERSION, PHASE_ESTABLISHED);
header[0] = CommonPrefix::ver_phase_byte(FMP_VERSION, PHASE_ESTABLISHED);
header[1] = flags;
header[2..4].copy_from_slice(&payload_len.to_le_bytes());
header[4..8].copy_from_slice(&receiver_idx.to_le_bytes());