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)
This commit is contained in:
Johnathan Corgan
2026-02-17 04:50:04 +00:00
parent d3477a7c0f
commit d46dc874ef
20 changed files with 3925 additions and 5759 deletions

View File

@@ -3,9 +3,9 @@
## What is FIPS?
FIPS is a self-organizing mesh network that can operate over any transport
mediumradio, serial links, Tor, local networks, or the existing internet as
an overlay. The long-term goal is infrastructure that can function alongside
or ultimately replace dependence on the Internet.
mediumradio, serial links, Tor, local networks, or the existing internet as
an overlay. The long-term goal is infrastructure that can function alongside or
ultimately replace dependence on the Internet.
Nodes in the mesh route traffic for each other using Nostr identities (npubs)
as network addresses. Applications can access the mesh through a native FIPS
@@ -15,7 +15,7 @@ as an IPv6 endpoint for compatibility with existing IP-based applications.
## Why FIPS?
**Infrastructure independence**: The internet depends on centralized
infrastructureISPs, backbone providers, DNS, certificate authorities. FIPS
infrastructureISPs, backbone providers, DNS, certificate authorities. FIPS
works over any transport that can carry packets: a LoRa radio link between
mountain towns, a serial cable between air-gapped systems, onion-routed
connections through Tor, or the existing internet as an overlay. When the
@@ -38,80 +38,185 @@ and security credentials without coordination with any central authority. The
identity system uses Nostr keypairs (secp256k1), so existing npub/nsec pairs
work directly.
## How It Works (Overview)
## A Self-Organizing Mesh
Each FIPS node selects which transports to use (Ethernet, radio links, internet
overlay, etc.) and which peer nodes to connect to. From these local peering
decisions, the distributed spanning tree and bloom filter propagation algorithms
self-organize reachability and path information throughout the mesh.
Traditional networks are built top-down. A central authority assigns addresses,
configures routing tables, provisions hardware, and manages the topology. If the
authority disappears or the infrastructure fails, the network fails with it.
Nodes cannot reach each other without infrastructure mediating the connection.
Nodes form a spanning tree rooted at a deterministically-elected node. Each
node knows its path to the root, enabling shortest-path routing to be
calculated between any two nodes (not necessarily through the root). Bloom filters propagate reachability information so nodes can find
each other. Traffic flows via greedy routing toward destinations, encrypted
end-to-end.
FIPS inverts this model. There is no central authority, no address assignment
service, no routing table pushed from above. Each node generates its own
identity from a cryptographic keypair. Each node independently decides which
peers to connect to and which transports to use. From these local decisions
alone, the network self-organizes:
Nodes with multiple transports automatically bridge between networks—the same
routing logic works regardless of the underlying transport mix.
- A **spanning tree** forms through distributed parent selection, giving every
node a coordinate in the network without any node knowing the full topology
- **Bloom filters** propagate through gossip, so each node learns which peers
can reach which destinations — again without global knowledge
- **Routing decisions** are made locally at each hop, using only the node's
immediate peers and cached coordinate information
The result is a network that builds itself from the bottom up, heals around
failures automatically, and scales without central coordination. Adding a node
is as simple as connecting to one existing peer — the network integrates the
new node through its normal gossip protocols.
## Design Goals
1. **Nostr-native identity** - Use Nostr keypairs as node identities
2. **Transport agnostic** - Support IP, wireless, serial, onion, and other link types
3. **Self-organizing** - Automatic topology discovery and route optimization
4. **Privacy preserving** - Minimize metadata leakage across untrusted links
5. **Resilient** - Self-healing with graceful degradation
6. **Reuse Nostr primitives** - Leverage secp256k1, Schnorr signatures, and SHA-256
1. **Nostr-native identity** Use Nostr keypairs as node identities
2. **Transport agnostic** Support IP, wireless, serial, onion, and other
link types
3. **Self-organizing** — Automatic topology discovery and route optimization
4. **Privacy preserving** — Minimize metadata leakage across untrusted links
5. **Resilient** — Self-healing with graceful degradation
6. **Reuse Nostr primitives** — Leverage secp256k1, Schnorr signatures, and
SHA-256
---
## Protocol Architecture
FIPS is organized in three protocol layers, each with distinct responsibilities
and clean service boundaries. Understanding these layers is key to understanding
how FIPS works.
```text
┌─────────────────────────────────────────────────────────────┐
│ Applications │
│ (native FIPS API / IPv6 adapter) │
├─────────────────────────────────────────────────────────────┤
│ │
│ FIPS Session Protocol (FSP) │
│ End-to-end authenticated encryption between endpoints │
│ Session lifecycle, coordinate caching, replay protection │
│ │
├─────────────────────────────────────────────────────────────┤
│ │
│ FIPS Link Protocol (FLP) │
│ Hop-by-hop link encryption, peer authentication │
│ Spanning tree, bloom filters, routing, forwarding │
│ │
├─────────────────────────────────────────────────────────────┤
│ │
│ Transport Layer │
│ Datagram delivery over arbitrary media │
│ UDP, Ethernet, LoRa, Tor, serial, ... │
│ │
└─────────────────────────────────────────────────────────────┘
```
### Mapping to Traditional Networking
Readers familiar with the OSI model or TCP/IP networking may find it helpful to
see how FIPS concepts relate to traditional layers:
```text
Traditional FIPS Key Difference
─────────────────────────────────────────────────────────────────────
Application Applications Same role — user-facing
(IPv6 adapter) software
─────────────────────────────────────────────────────────────────────
Transport (TCP/UDP) (not present) FIPS provides datagrams,
not reliable streams
─────────────────────────────────────────────────────────────────────
Session FSP End-to-end encryption
and session management
─────────────────────────────────────────────────────────────────────
Network (IP) FLP Routing, forwarding,
address resolution —
but self-organizing
─────────────────────────────────────────────────────────────────────
Link (Ethernet/WiFi) FLP (link encryption) Peer authentication
and hop-by-hop crypto
─────────────────────────────────────────────────────────────────────
Physical (PHY) Transport layer Abstracted — FIPS
(UDP, radio, serial) treats all media the
same way
─────────────────────────────────────────────────────────────────────
```
Note that FLP spans what would traditionally be separate link and network
layers. This is intentional — in a self-organizing mesh, the same layer that
authenticates peers also makes routing decisions, because routing depends on
authenticated peer state (spanning tree positions, bloom filters).
### Layer Responsibilities
**Transport layer**: Delivers datagrams between endpoints over a specific
medium. Each transport type (UDP socket, Ethernet interface, radio modem)
implements the same abstract interface: send and receive datagrams, report MTU.
The transport layer knows nothing about FIPS identities, routing, or encryption.
It provides raw datagram delivery to FLP above.
See [fips-transport-layer.md](fips-transport-layer.md) for the transport layer
specification.
**FIPS Link Protocol (FLP)**: Manages peer connections, authenticates peers via
Noise IK handshakes, and encrypts all traffic on each link. FLP is where the
mesh organizes itself — nodes exchange spanning tree announcements and bloom
filters with their direct peers, and FLP makes forwarding decisions for transit
traffic. FLP provides authenticated, encrypted forwarding to FSP above.
See [fips-link-layer.md](fips-link-layer.md) for the FLP specification and
[fips-mesh-operation.md](fips-mesh-operation.md) for how FLP's routing and
self-organization work in practice.
**FIPS Session Protocol (FSP)**: Provides end-to-end authenticated encryption
between any two nodes, regardless of how many intermediate hops separate them.
FSP manages session lifecycle (setup, data transfer, teardown), caches
destination coordinates for efficient routing, and handles the warmup strategy
that keeps transit node caches populated. FSP provides a datagram service to
applications above.
See [fips-session-layer.md](fips-session-layer.md) for the FSP specification.
**IPv6 adaptation layer**: Sits above FSP and adapts the FIPS datagram service
for unmodified IPv6 applications. Provides DNS resolution (npub → fd::/8
address), identity cache management, MTU enforcement, and a TUN interface.
This is the primary way existing applications use the FIPS mesh.
See [fips-ipv6-adapter.md](fips-ipv6-adapter.md) for the IPv6 adapter.
---
## Architecture Overview
![Architecture Overview](fips-architecture-overview.svg)
Each link uses a different transport, but the end-to-end session encryption
is independent of the transport mix. Intermediate nodes decrypt the link
layer to make routing decisions, then re-encrypt for the next hop. They
cannot read the session-layer payload.
Each link uses a different transport, but the end-to-end session encryption is
independent of the transport mix. Intermediate nodes decrypt the link layer to
make routing decisions, then re-encrypt for the next hop. They cannot read the
session-layer payload.
Applications can use the native FIPS datagram service directly, or access the
mesh through an IPv6 adaptation layer (TUN device) for compatibility with
existing IP-based applications. The router handles discovery, routing, and
encryption transparently in either case.
```text
Application ──────────── End-to-end FSP session ──────────── Application
│ │
▼ ▼
┌─────────┐ FLP link ┌─────────┐ FLP link ┌─────────┐
│ Node A │◄──────────────►│ Node B │◄──────────────►│ Node C │
└────┬────┘ (Noise IK) └────┬────┘ (Noise IK) └────┬────┘
│ │ │
UDP/IP Ethernet LoRa
transport transport transport
```
See [fips-transports.md](fips-transports.md) for transport options and characteristics.
Each FLP link operates over its own transport type, with independent link-layer
encryption. The FSP session spans the entire path, providing end-to-end
confidentiality that is independent of the transport mix along the route.
![Node Architecture](fips-node-architecture.svg)
Internally, each node is organized in three layers. At the top, two application
interfaces provide access to the mesh: a native datagram API addressed by npub,
and an IPv6 TUN adapter that maps npubs to `fd::/8` addresses so unmodified
IP applications can use the network transparently. The router core in the middle
implements spanning tree maintenance, bloom filter exchange, greedy routing,
session management, and Noise IK encryption. At the bottom, transport plugins
handle the physical diversity — each plugin implements the same interface, so the
router treats UDP, Ethernet, LoRa, Tor, and serial links identically. Adding a
new transport requires no changes to the routing or session layers.
## Prior Work
FIPS builds on proven designs rather than inventing new cryptography or routing
algorithms.
**Routing**: The spanning tree coordinates, bloom filter discovery, and greedy
routing algorithms are adapted from [Yggdrasil v0.5](https://yggdrasil-network.github.io/2023/10/22/upcoming-v05-release.html)
and its [Ironwood](https://github.com/Arceliar/ironwood) routing library. FIPS
adapts these for multi-transport operation and Nostr identity integration.
**Encryption**: Link and session encryption use the [Noise Protocol
Framework](https://noiseprotocol.org/), the same foundation used by WireGuard,
Lightning Network, and other production systems. FIPS uses the IK pattern for
link authentication and end-to-end sessions.
**Cryptographic primitives**: FIPS reuses Nostr's cryptographic stack—secp256k1
for keys, Schnorr signatures, SHA-256 for hashing, and ChaCha20-Poly1305 for
authenticated encryption. No novel cryptography.
**Session management**: The index-based session dispatch follows WireGuard's
approach, enabling O(1) packet routing without relying on source addresses.
Internally, each node is organized in the three protocol layers. At the top,
two application interfaces provide access to the mesh: a native datagram API
addressed by npub, and an IPv6 TUN adapter that maps npubs to `fd::/8`
addresses so unmodified IP applications can use the network transparently. The
FSP and FLP layers in the middle implement session management, routing, and
encryption. At the bottom, transport plugins handle the physical diversity —
each plugin implements the same interface, so the router treats UDP, Ethernet,
LoRa, Tor, and serial links identically. Adding a new transport requires no
changes to the routing or session layers.
---
@@ -125,7 +230,7 @@ The FIPS address (synonymous with the pubkey) is the primary means for
application-layer software to identify communication endpoints. The
bech32-encoded npub can be used interchangeably for user interface purposes.
The FIPS datagram service is exposed to the application layer either via a
native API to the FIPS node software, or through an IPv6 shim driver that
native API to the FIPS node software, or through an IPv6 adaptation layer that
converts the node identity into an IPv6 address and provides DNS resolution
from npub to this address for traditional software.
@@ -133,70 +238,70 @@ from npub to this address for traditional software.
![Identity Derivation](fips-identity-derivation.svg)
The pubkey is the node's cryptographic identity, used in Noise IK handshakes for
both link and session encryption. It is never exposed beyond the endpoints of an
encrypted channel. The node_addr, a one-way SHA-256 hash truncated to 16 bytes,
serves as the routing identifier in packet headers and bloom filters. Intermediate
routers see only node_addrs — they can forward traffic without learning the Nostr
identities of the endpoints. An observer can verify "does this node_addr belong
to pubkey X?" but cannot enumerate which pubkeys are communicating by inspecting
traffic. The IPv6 address prepends `fd` to the first 15 bytes of the node_addr,
providing an overlay address for unmodified IP applications via the TUN interface.
The pubkey is the node's cryptographic identity, used in Noise IK handshakes
for both link and session encryption. It is never exposed beyond the endpoints
of an encrypted channel. The node_addr, a one-way SHA-256 hash truncated to
16 bytes, serves as the routing identifier in packet headers and bloom filters.
Intermediate routers see only node_addrs — they can forward traffic without
learning the Nostr identities of the endpoints. An observer can verify "does
this node_addr belong to pubkey X?" but cannot enumerate which pubkeys are
communicating by inspecting traffic. The IPv6 address prepends `fd` to the
first 15 bytes of the node_addr, providing an overlay address for unmodified
IP applications via the TUN interface.
### Address Format
When using the IPv6 protocol adapter, FIPS addresses use the IPv6 Unique Local
When using the IPv6 adaptation layer, FIPS addresses use the IPv6 Unique Local
Address (ULA) prefix `fd00::/8`, providing 120 bits from the node_addr hash.
These are overlay identifiersthey appear in the TUN interface for application
These are overlay identifiersthey appear in the TUN interface for application
compatibility but are not routable on the underlying transport. The fd prefix
ensures no collision with addresses that may be in use on the transport network.
FIPS provides a local DNS service that maps npub bech32 names to IPv6 addresses
for this purpose.
### Identity Verification
The Noise Protocol Framework is used to mutually authenticate both peer-to-peer
link connections and end-to-end session traffic, proving each party controls
the private key for their claimed identity.
link connections (at FLP) and end-to-end session traffic (at FSP), proving each
party controls the private key for their claimed identity.
See [fips-wire-protocol.md](fips-wire-protocol.md) for the Noise IK handshake
and [fips-session-protocol.md](fips-session-protocol.md) for end-to-end
session establishment.
See [fips-link-layer.md](fips-link-layer.md) for peer authentication and
[fips-session-layer.md](fips-session-layer.md) for end-to-end session
establishment.
### Terminology: Addresses and Identifiers
FIPS uses several related but distinct identifiers at different protocol layers:
| Term | Layer | Visible To | Description |
|----------------------------|---------------------|----------------|----------------------------------------------------------------------|
| **FIPS address / pubkey** | Application/Session | Endpoints only | 32-byte secp256k1 public key - the endpoint identity |
| **npub** | (encoding) | Human readers | Bech32 encoding of pubkey for display/config |
| **node_addr** | Routing | Routing nodes | SHA-256(pubkey) truncated to 128 bits - cannot be reversed to pubkey |
| **link_addr** | Transport | Direct peers | IP:port, MAC, .onion - transport-specific |
| **IPv6 address** | IPv6 shim | Applications | fd::/8 derived from node_addr - optional compatibility |
| Term | Layer | Visible To | Description |
| ---- | ----- | ---------- | ----------- |
| **FIPS address / pubkey** | Application/FSP | Endpoints only | 32-byte secp256k1 public key the endpoint identity |
| **npub** | (encoding) | Human readers | Bech32 encoding of pubkey for display/config |
| **node_addr** | FLP (routing) | Routing nodes | SHA-256(pubkey) truncated to 128 bits cannot be reversed to pubkey |
| **link_addr** | Transport | Direct peers | IP:port, MAC, .onion transport-specific |
| **IPv6 address** | IPv6 adapter | Applications | fd::/8 derived from node_addr optional compatibility |
**Privacy property**: The pubkey (FIPS address / Nostr identity) is never exposed to
intermediate routing nodes. They see only the node_addr, a one-way hash. An observer
can verify "does this node_addr belong to pubkey X?" but cannot derive the pubkey from
traffic.
**Privacy property**: The pubkey (FIPS address / Nostr identity) is never
exposed to intermediate routing nodes. They see only the node_addr, a one-way
hash. An observer can verify "does this node_addr belong to pubkey X?" but
cannot derive the pubkey from traffic.
---
## Two-Layer Encryption
FIPS uses independent encryption at two layers:
FIPS uses independent encryption at two protocol layers:
| Layer | Scope | Pattern | Purpose |
|-------------|-------------|----------|------------------------------------------------|
| **Link** | Hop-by-hop | Noise IK | Encrypt all traffic on each link |
| **Session** | End-to-end | Noise IK | Encrypt application payload between endpoints |
| Layer | Scope | Pattern | Purpose |
| ----- | ----- | ------- | ------- |
| **FLP (Link)** | Hop-by-hop | Noise IK | Encrypt all traffic on each peer link |
| **FSP (Session)** | End-to-end | Noise IK | Encrypt application payload between endpoints |
### Link Layer (Hop-by-Hop)
When two nodes establish a direct connection, they perform a Noise IK handshake.
This authenticates both parties and establishes symmetric keys for encrypting
all traffic on that link. Every packet between direct peers is encrypted—gossip
messages, routing queries, and forwarded session datagrams alike.
When two nodes establish a direct connection, they perform a Noise IK
handshake. This authenticates both parties and establishes symmetric keys for
encrypting all traffic on that link. Every packet between direct peers is
encrypted — gossip messages, routing queries, and forwarded session datagrams
alike.
The IK pattern is used because outbound connections know the peer's npub from
configuration, while inbound connections learn the initiator's identity from
@@ -207,188 +312,130 @@ the first handshake message.
FIPS establishes end-to-end encrypted sessions between any two communicating
nodes using Noise IK, regardless of whether they are direct peers or separated
by intermediate routers. The initiator knows the destination's npub; the
responder learns the initiator's identity from the handshakethe same asymmetry
as link-layer connections.
responder learns the initiator's identity from the handshakethe same
asymmetry as link-layer connections.
Both layers always apply. For adjacent peers, application traffic is encrypted
twice: once by the session layer (end-to-end) and once by the link layer
(hop-by-hop). This uniform model means:
- No special case for "local peer" vs "remote destination"
- Topology changes (a direct peer becomes reachable only through intermediaries)
don't affect sessions
- Topology changes (a direct peer becomes reachable only through
intermediaries) don't affect sessions
- The link layer remains purely a transport concern
A packet from A to adjacent peer B:
1. A encrypts payload with A↔B session key
2. A wraps in SessionDatagram, encrypts with A↔B link key, sends to B
1. A encrypts payload with A↔B session key (FSP)
2. A wraps in SessionDatagram, encrypts with A↔B link key (FLP), sends to B
3. B decrypts link layer, then decrypts session layer to get payload
A packet from A to D through intermediate node B:
1. A encrypts payload with A↔D session key
2. A wraps in SessionDatagram, encrypts with A↔B link key, sends to B
3. B decrypts link layer, sees destination, re-encrypts with B↔D link key
1. A encrypts payload with A↔D session key (FSP)
2. A wraps in SessionDatagram, encrypts with A↔B link key (FLP), sends to B
3. B decrypts link layer, reads destination, re-encrypts with B↔D link key
4. D decrypts link layer, then decrypts session layer to get payload
Intermediate nodes can route based on destination address but cannot read
Intermediate nodes can route based on destination node_addr but cannot read
session-layer payloads.
FIPS session setup also warms up route caches along the path between the
endpoints, so that when application traffic flows the network is already ready.
See [fips-wire-protocol.md](fips-wire-protocol.md) for link encryption and
[fips-session-protocol.md](fips-session-protocol.md) for session encryption.
See [fips-link-layer.md](fips-link-layer.md) for link encryption and
[fips-session-layer.md](fips-session-layer.md) for session encryption.
---
## Spanning Tree Protocol
## Routing and Mesh Operation
FIPS routing is entirely distributed — each node makes forwarding decisions
using only local information. There are no routing tables pushed from above, no
link-state floods, and no distance-vector exchanges. Instead, two complementary
mechanisms provide the information each node needs.
### Spanning Tree: The Coordinate System
![Mesh Topology](fips-mesh-topology.svg)
The spanning tree is a subset of the full mesh network that connects all nodes,
forming a tree structure rooted at a deterministically-elected node. Each node
selects a single parent, and the resulting tree serves as the routing backbone.
This enables routing without global routing tables.
Nodes self-organize into a spanning tree rooted at a deterministically-elected
node (the one with the smallest node_addr). Each node selects a single parent
from among its direct peers, and the resulting tree gives every node a
**coordinate** — its path from itself to the root.
### Why a Spanning Tree?
These coordinates enable distance calculations between any two nodes: the
distance is the number of hops from each node to their lowest common ancestor
in the tree. This provides a metric for routing decisions without any node
needing to know the full network topology.
A spanning tree has useful properties:
The tree maintains itself through gossip — nodes exchange TreeAnnounce messages
with their peers, propagating parent selections and ancestry chains. Changes
cascade through the tree proportional to depth, not network size. If the
network partitions, each segment elects its own root and reconverges
automatically when segments rejoin.
- **Unique paths**: Exactly one path exists between any two nodes
- **Minimal state**: Nodes only track their parent and immediate peers
- **Coordinates**: A node's position in the tree enables distance calculations
See [fips-spanning-tree.md](fips-spanning-tree.md) for the tree algorithms and
[spanning-tree-dynamics.md](spanning-tree-dynamics.md) for detailed convergence
walkthroughs.
The tree provides structure for routing while bloom filters provide reachability
information. Together they enable efficient packet delivery without requiring
nodes to know the full network topology.
### Bloom Filters: Candidate Selection
### Tree Coordinates
Each node maintains bloom filters summarizing which destinations are reachable
through each of its peers. Bloom filters propagate via gossip, with each node
computing outbound filters by merging the filters received from its other
peers. At steady state, filters represent the entire reachable network.
A node's coordinate is its path from itself to the root. The distance between
two nodes is the sum of hops from each to their lowest common ancestor (LCA).
This distance metric enables greedy routing: forward packets to the peer that
minimizes distance to the destination.
Bloom filters answer a single question: "can peer P possibly reach destination
D?" The answer is either "no" (definitive) or "maybe" (probabilistic — false
positives are possible). This is **candidate selection**, not routing — bloom
filters identify which peers are worth considering, but the actual forwarding
decision requires tree coordinates to rank those candidates by distance.
### Root Election
See [fips-bloom-filters.md](fips-bloom-filters.md) for filter parameters and
mathematical properties.
The root is the node with the lexicographically smallest node_addr among all
reachable nodes. This election is deterministic and requires no coordination—
each node independently examines its view of the network and reaches the same
conclusion.
### Routing Decisions
The root provides a coordinate reference point but does not participate in
routing unless the paths from source and destination to the root share no
common ancestors (i.e., the root is their lowest common ancestor).
At each hop, FLP makes a local forwarding decision using the following priority
chain:
### Parent Selection
1. **Local delivery** — the destination is this node
2. **Direct peer** — the destination is an authenticated neighbor
3. **Bloom-guided candidate selection** — bloom filters identify peers that can
reach the destination; tree coordinates rank them by distance
4. **Greedy tree routing** — fallback when bloom filters haven't converged;
forward to the peer that minimizes tree distance to the destination
5. **No route** — destination unreachable; send error signal to source
Each node selects a parent that provides the best path to root, considering:
All multi-hop routing depends on knowing the destination's tree coordinates.
These are cached at each node after being learned through discovery
(LookupRequest/LookupResponse) or session establishment (SessionSetup). The
coordinate cache is the critical piece that enables efficient forwarding.
- Reachability (the parent must have a path to root)
- Link quality (latency, packet loss, bandwidth)
- Stability (hysteresis prevents flapping on minor changes)
### Coordinate Caching and Discovery
The parent must be a direct peer—nodes cannot select non-peers as parents.
When a node first needs to reach an unknown destination, it sends a
LookupRequest that floods through the network guided by bloom filters. The
destination responds with its coordinates, which the source caches. Subsequent
traffic routes efficiently using the cached coordinates.
### Tree Gossip
Session establishment (SessionSetup) also carries coordinates, warming transit
node caches along the path so that data packets can be forwarded without
individual discovery at each hop.
Nodes exchange TreeAnnounce messages containing their parent selection and
ancestry chain (path to root). When a node changes its parent, it announces the
change; peers propagate relevant updates. The tree converges through this
gossip without centralized coordination.
### Error Recovery
Changes propagate only as far as they need to—distantly connected nodes are
unaffected by local path changes and don't receive updates for them.
When routing fails — because cached coordinates are stale or a path has
broken — transit nodes signal the source:
### Partition Handling
- **CoordsRequired**: A transit node lacks the destination's coordinates.
The source re-initiates discovery and resets its coordinate warmup strategy.
- **PathBroken**: Greedy routing reached a dead end. The source re-discovers
the destination's current coordinates.
If the network partitions, each isolated segment elects its own root (the
smallest node_addr within that segment). When partitions merge, nodes in the
segment with the larger root discover the globally smaller root and re-parent.
The tree reconverges automatically.
Both signals trigger active recovery, and are rate-limited to prevent storms
during topology changes.
See [fips-routing.md](fips-routing.md) for routing concepts,
[fips-gossip-protocol.md](fips-gossip-protocol.md) for message formats, and
[spanning-tree-dynamics.md](spanning-tree-dynamics.md) for convergence behavior.
---
## Bloom Filter Routing
Tree coordinates enable routing once you know a destination's position. Bloom
filters enable finding that position in the first place.
A bloom filter is a space-efficient probabilistic data structure that can test
whether an element is a member of a set. It may produce false positives (saying
an element is present when it isn't) but never false negatives. This makes it
ideal for routing: a node can quickly check if a destination might be reachable
through a given peer, with occasional false positives handled by backtracking.
### How It Works
Each node maintains bloom filters summarizing which node_addrs are reachable
through each of its peers. These filters propagate through the tree: a node
aggregates filters from its children and announces the combined filter to its
parent (and vice versa).
When a node needs to reach an unknown destination:
1. Check local bloom filters—which peers might be able to reach this node_addr?
2. Send a LookupRequest to peers whose filters indicate "maybe"
3. The request propagates through the tree toward matching subtrees
4. The destination responds with a LookupResponse containing its coordinates
5. The sender caches the coordinates and routes directly via greedy forwarding
Bloom filters have false positives (a filter may indicate "maybe" when the node
isn't actually reachable through that path) but no false negatives. Extra
queries are harmless; missing a reachable node is not.
### Filter Propagation
Filters propagate in the opposite direction from tree announcements:
- Tree state propagates upward (toward root) via ancestry chains
- Bloom filters propagate downward (toward leaves) via subtree aggregation
A node's filter contains all node_addrs reachable through its subtree. The root's
filter contains everyone; leaf nodes have empty outbound filters.
See [fips-routing.md](fips-routing.md) for bloom filter design and
[fips-gossip-protocol.md](fips-gossip-protocol.md) for FilterAnnounce format.
---
## Greedy Routing
Once a destination's tree coordinates are known, packets are forwarded using
greedy routing: at each hop, forward to the peer that minimizes tree distance
to the destination.
### The Algorithm
1. If I am the destination, deliver the packet locally
2. Calculate my tree distance to the destination
3. For each peer, calculate their tree distance to the destination
4. Forward to the peer with the smallest distance (must be less than mine)
5. If no peer is closer, routing has failed (local minimum)
### Path-Broken Recovery
Greedy routing can fail if the destination has moved or the cached coordinates
are stale. When this happens, the node that cannot make progress sends a
PathBroken notification back to the source. The source then initiates a fresh
bloom filter lookup to find the destination's current coordinates.
### Session Establishment
For efficiency, FIPS establishes routing sessions that cache coordinate
information at intermediate routers. The first packet (SessionSetup) carries
full coordinates; subsequent packets use cached state for minimal overhead.
See [fips-routing.md](fips-routing.md) for the complete routing design and
[fips-session-protocol.md](fips-session-protocol.md) for session establishment.
See [fips-mesh-operation.md](fips-mesh-operation.md) for the complete routing
and mesh behavior description.
---
@@ -397,84 +444,33 @@ See [fips-routing.md](fips-routing.md) for the complete routing design and
FIPS is transport-agnostic. The protocol operates identically whether peers
connect over UDP, Ethernet, LoRa radio, serial cables, or Tor hidden services.
### Transports and Links
A **transport** is a physical or logical interface: a UDP socket, an Ethernet
NIC, a Tor client, a radio modem. A **link** is a connection instance to a
specific peer over a transport.
### Multi-Transport Bridging
NIC, a Tor client, a radio modem. A **link** is a peer connection established
over a transport. Transport addresses (IP:port, MAC address, .onion) are opaque
to all layers above FLP — they are used only to deliver datagrams and are
discarded once FLP has authenticated the peer.
A node with multiple transports automatically bridges between networks. Peers
from all transports feed into a single spanning tree; the router selects the
best path regardless of transport type. If one transport fails, traffic
automatically routes through alternatives.
### Transport Types
| Category | Examples | Characteristics |
|----------|----------|-----------------|
| Overlay | UDP/IP, TCP/TLS, QUIC, WebSocket | Internet connectivity, NAT considerations |
| Shared medium | Ethernet, WiFi, Bluetooth, LoRa | Broadcast/multicast discovery |
| Point-to-point | Serial, dialup | No discovery needed, static config |
| -------- | -------- | --------------- |
| Overlay | UDP/IP, TCP/TLS, WebSocket | Internet connectivity, NAT traversal |
| Shared medium | Ethernet, WiFi, Bluetooth, LoRa | Local discovery, broadcast |
| Point-to-point | Serial, dialup | Static config, no discovery |
| Anonymity | Tor, I2P | High latency, strong privacy |
UDP over IP is expected to be the most common transport for internet-connected
nodes. Radio transports enable connectivity where internet infrastructure is
unavailable.
> **Implementation status**: UDP/IP is the only implemented transport. All
> others are future directions.
See [fips-transports.md](fips-transports.md) for transport characteristics.
See [fips-transport-layer.md](fips-transport-layer.md) for transport layer
design.
---
## Protocol Messages
FIPS uses a discriminator-based wire format for efficient message dispatch.
### Link Layer Messages
Exchanged between directly connected peers, encrypted with link session keys:
| Type | Name | Purpose |
|------|----------------|------------------------------------------------------|
| 0x10 | TreeAnnounce | Spanning tree state (parent, ancestry) |
| 0x20 | FilterAnnounce | Bloom filter reachability update |
| 0x30 | LookupRequest | Query for node's tree coordinates |
| 0x31 | LookupResponse | Response with coordinates and proof |
| 0x40 | SessionDatagram| Carries session-layer payloads through the mesh |
| 0x50 | Disconnect | Orderly disconnect notification |
### SessionDatagram Payload Messages
Carried inside SessionDatagram (0x40), which provides src_addr, dest_addr, and
hop_limit for multi-hop forwarding.
**Session-layer messages** (end-to-end encrypted between source and destination):
| Type | Name | Purpose |
|------|----------------|--------------------------------------------|
| 0x00 | SessionSetup | Establish routing session with coordinates |
| 0x01 | SessionAck | Acknowledge session establishment |
| 0x10 | DataPacket | Encrypted application data (IPv6 payload) |
**Link-layer error signals** (plaintext, generated by transit routers):
| Type | Name | Purpose |
|------|----------------|--------------------------------------------|
| 0x20 | CoordsRequired | Router cache miss—need fresh coordinates |
| 0x21 | PathBroken | Greedy routing failed—need re-lookup |
Error signals are sent back to the original source using the `src_addr` from
the SessionDatagram that triggered the error. They are plaintext because the
transit router has no end-to-end session with the source; link-layer encryption
protects them hop-by-hop.
See [fips-wire-protocol.md](fips-wire-protocol.md) for wire format details and
[fips-gossip-protocol.md](fips-gossip-protocol.md) for gossip message formats.
---
## Security Considerations
## Security
### Threat Model
@@ -493,79 +489,105 @@ sees only encrypted packets.
**End-to-end encryption**: Session-layer Noise IK encrypts payloads between
endpoints. Intermediate routers cannot read application data.
**Signature verification**: All protocol messages (TreeAnnounce, LookupResponse)
are signed. The full ancestry chain in TreeAnnounce includes signatures from
each node, preventing forged tree positions.
**Signature verification**: TreeAnnounce messages carry signed parent
declarations. Direct peers verify signatures using keys established during
the Noise IK handshake; ancestry beyond direct peers uses transitive trust
in the v1 protocol.
**Replay protection**: Sequence numbers and timestamps on announcements.
Counter-based nonces with sliding window for encrypted packets.
**Replay protection**: Counter-based nonces with sliding window for encrypted
packets at both the link and session layers. Sequence numbers on protocol
announcements prevent replay of stale state.
### Sybil Resistance
Creating many identities is cheap, but exploiting them is constrained:
- **Discretionary peering**: Node operators choose who to peer with. An attacker
with many identities still needs real nodes to accept their connections.
- **Discretionary peering**: Node operators choose who to peer with. An
attacker with many identities still needs real nodes to accept their
connections.
- **Tree coordinate verification**: Nodes cannot claim arbitrary tree positions
without valid signed ancestry chains from real nodes
without valid signed ancestry chains from real nodes.
- **Rate limiting**: Handshake rate limiting constrains how fast attackers can
establish connections
establish connections.
### Metadata Exposure
Each entity in the network sees different information:
| Entity | Can See |
|--------|---------|
| ------ | ------- |
| Transport observer | Encrypted packets, timing, packet sizes |
| Direct peer | Your npub (identity), traffic volume, timing |
| Intermediate router | Source and destination node_addrs, packet size |
| Destination | Your npub (identity), payload content |
Intermediate routers see node_addrs, not npubs. Since node_addrs are derived from
pubkeys via one-way SHA-256 hash, routers cannot determine the actual identities
of the endpoints they route for.
Intermediate routers see node_addrs, not npubs. Since node_addrs are derived
from pubkeys via one-way SHA-256 hash, routers cannot determine the actual
identities of the endpoints they route for.
The session layer hides payload content from intermediate routers. The link
layer hides everything from passive observers on the underlying transport.
---
## Conclusion
## Prior Work
FIPS combines these elements into a cohesive system that achieves its design
goals:
FIPS builds on proven designs rather than inventing new cryptography or routing
algorithms.
- **Self-sovereign identity** through Nostr keypairs, with node_addrs providing
routing-level privacy
- **Transport agnosticism** via the transport abstraction layer, enabling the
same routing logic across UDP, Ethernet/WiFi, Tor, and other link types
- **Self-organization** through distributed spanning tree formation and bloom
filter propagation, requiring no central coordination
- **Privacy preservation** with two-layer encryption that hides payloads from
intermediate routers and hides everything from transport observers
- **Resilience** through automatic partition detection, re-election, and tree
reconvergence when the network topology changes
**Routing**: The spanning tree coordinates, bloom filter candidate selection,
and greedy routing algorithms are adapted from
[Yggdrasil v0.5](https://yggdrasil-network.github.io/2023/10/22/upcoming-v05-release.html)
and its [Ironwood](https://github.com/Arceliar/ironwood) routing library. FIPS
adapts these for multi-transport operation and Nostr identity integration.
The result is a mesh network where nodes can find and communicate with each
other securely, regardless of the underlying transport infrastructure, while
maintaining control over their own identities and peering relationships.
**Encryption**: Link and session encryption use the
[Noise Protocol Framework](https://noiseprotocol.org/), the same foundation
used by WireGuard, Lightning Network, and other production systems. FIPS uses
the IK pattern for both link authentication and end-to-end sessions.
**Cryptographic primitives**: FIPS reuses Nostr's cryptographic stack —
secp256k1 for keys, Schnorr signatures, SHA-256 for hashing, and
ChaCha20-Poly1305 for authenticated encryption. No novel cryptography.
**Session management**: The index-based session dispatch follows WireGuard's
approach, enabling O(1) packet routing without relying on source addresses.
---
## References
## Further Reading
### FIPS Design Documents
### Protocol Layers
| Document | Description |
|----------|-------------|
| [fips-session-protocol.md](fips-session-protocol.md) | End-to-end session flow, Noise IK encryption |
| [fips-wire-protocol.md](fips-wire-protocol.md) | Link-layer transport, Noise IK handshake |
| [fips-gossip-protocol.md](fips-gossip-protocol.md) | TreeAnnounce, FilterAnnounce, Lookup formats |
| [fips-routing.md](fips-routing.md) | Bloom filters, discovery, greedy routing |
| [spanning-tree-dynamics.md](spanning-tree-dynamics.md) | Tree protocol dynamics and convergence |
| [fips-transports.md](fips-transports.md) | Transport protocol characteristics |
| [fips-software-architecture.md](fips-software-architecture.md) | Software architecture, configuration |
| -------- | ----------- |
| [fips-transport-layer.md](fips-transport-layer.md) | Transport layer: abstraction, types, services provided to FLP |
| [fips-link-layer.md](fips-link-layer.md) | FLP: peer authentication, link encryption, forwarding |
| [fips-session-layer.md](fips-session-layer.md) | FSP: end-to-end encryption, session lifecycle |
| [fips-ipv6-adapter.md](fips-ipv6-adapter.md) | IPv6 adaptation: DNS, TUN interface, MTU enforcement |
### Mesh Behavior and Wire Formats
| Document | Description |
| -------- | ----------- |
| [fips-mesh-operation.md](fips-mesh-operation.md) | How the mesh operates: routing, discovery, error recovery |
| [fips-wire-formats.md](fips-wire-formats.md) | Complete wire format reference for all protocol layers |
### Supporting References
| Document | Description |
| -------- | ----------- |
| [fips-spanning-tree.md](fips-spanning-tree.md) | Spanning tree algorithms and data structures |
| [fips-bloom-filters.md](fips-bloom-filters.md) | Bloom filter parameters, math, and computation |
| [spanning-tree-dynamics.md](spanning-tree-dynamics.md) | Scenario walkthroughs: convergence, partitions, recovery |
### Implementation
| Document | Description |
| -------- | ----------- |
| [fips-software-architecture.md](fips-software-architecture.md) | Stable architectural decisions guiding the codebase |
| [fips-state-machines.md](fips-state-machines.md) | Phase-based state machine pattern (Rust) |
| [fips-configuration.md](fips-configuration.md) | YAML configuration reference |
### External References