mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
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.
This commit is contained in:
+14
-20
@@ -7,11 +7,11 @@ Protocol design specifications and analysis for the Federated Interoperable Peer
|
||||
Start with the high-level architecture, then work through session flow, routing
|
||||
concepts, and finally the wire-level protocol details.
|
||||
|
||||
### 1. Architecture and Overview
|
||||
### 1. Introduction and Overview
|
||||
|
||||
| Document | Description |
|
||||
|------------------------------------------|-------------------------------------------------------------|
|
||||
| [fips-design.md](fips-design.md) | Core protocol: goals, identity, addressing, spanning tree |
|
||||
| [fips-intro.md](fips-intro.md) | Protocol introduction: goals, concepts, architecture |
|
||||
|
||||
### 2. Protocol Flow (How Traffic Works)
|
||||
|
||||
@@ -45,22 +45,16 @@ concepts, and finally the wire-level protocol details.
|
||||
## Document Cross-References
|
||||
|
||||
```text
|
||||
fips-design.md
|
||||
│
|
||||
▼
|
||||
fips-session-protocol.md
|
||||
│ │
|
||||
┌─────────┘ └─────────┐
|
||||
▼ ▼
|
||||
fips-routing.md fips-wire-protocol.md
|
||||
│ │
|
||||
▼ │
|
||||
spanning-tree-dynamics.md │
|
||||
│ │
|
||||
└───────────┬───────────────────┘
|
||||
▼
|
||||
fips-gossip-protocol.md
|
||||
│
|
||||
▼
|
||||
fips-transports.md
|
||||
fips-intro.md
|
||||
│
|
||||
┌────────────┴────────────┐
|
||||
▼ ▼
|
||||
fips-session-protocol.md fips-architecture.md
|
||||
│ │
|
||||
┌─────────┴─────────┐ ▼
|
||||
▼ ▼ fips-transports.md
|
||||
fips-routing.md fips-wire-protocol.md
|
||||
│ │
|
||||
▼ ▼
|
||||
spanning-tree-dynamics.md ←→ fips-gossip-protocol.md
|
||||
```
|
||||
|
||||
@@ -1040,7 +1040,7 @@ establishment).
|
||||
|
||||
## References
|
||||
|
||||
- [fips-design.md](fips-design.md) — Overall FIPS protocol design
|
||||
- [fips-intro.md](fips-intro.md) — Overall FIPS protocol design
|
||||
- [fips-session-protocol.md](fips-session-protocol.md) — Traffic flow, session terminology, crypto sessions
|
||||
- [fips-transports.md](fips-transports.md) — Transport protocol characteristics
|
||||
- [fips-routing.md](fips-routing.md) — Routing, Bloom filters, discovery
|
||||
|
||||
@@ -1,790 +0,0 @@
|
||||
# FIPS: Federated Interoperable Peering System
|
||||
|
||||
A distributed, decentralized network routing protocol for mesh nodes connecting
|
||||
over arbitrary transports. Inspired by [Yggdrasil v0.5](https://yggdrasil-network.github.io/2023/10/22/upcoming-v05-release.html)
|
||||
but adapted for the Nostr ecosystem with multi-transport flexibility.
|
||||
|
||||
## 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 cryptographic primitives already in use in
|
||||
the Nostr ecosystem (secp256k1, Schnorr signatures, SHA-256) to simplify
|
||||
implementation and reduce dependency surface
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Application Layer │
|
||||
│ (Nostr clients, services, bridges) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ FIPS Router │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ Identity │ │ Spanning │ │ Bloom Filter │ │
|
||||
│ │ (npub) │ │ Tree │ │ Routing Table │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Transport Abstraction │
|
||||
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
|
||||
│ │ TCP │ │ QUIC │ │ Radio │ │ Serial │ │ Onion │ │
|
||||
│ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Identity System
|
||||
|
||||
### Node Identity
|
||||
|
||||
FIPS uses Nostr keypairs (secp256k1) directly as node identities. There is no need
|
||||
for the clustering properties that Yggdrasil's Ed25519 bit-inversion scheme provides;
|
||||
the spanning tree handles all routing structure.
|
||||
|
||||
Node addresses use an IPv6-compatible format to facilitate reuse of applications
|
||||
designed for IP transports (e.g., binding to a FIPS address via a TUN interface).
|
||||
This does not imply that peering over existing IPv4 or IPv6 networks is required—FIPS
|
||||
supports arbitrary transports including radio, serial, and other non-IP links.
|
||||
Nonetheless, it is anticipated that the majority of FIPS peers will connect via
|
||||
the public Internet.
|
||||
|
||||
### Node ID and Address Derivation
|
||||
|
||||
```text
|
||||
nostr_npub (secp256k1 x-only, 32 bytes)
|
||||
│
|
||||
▼ SHA-256
|
||||
node_id (32 bytes)
|
||||
│
|
||||
▼ Truncate with prefix
|
||||
fips_address (128 bits)
|
||||
```
|
||||
|
||||
The full 32-byte `node_id` is used in protocol messages and bloom filters.
|
||||
The truncated 128-bit address is used for IPv6 compatibility.
|
||||
|
||||
**Why hash the npub?** Secp256k1 public keys can be "ground" to achieve specific
|
||||
prefixes more efficiently than brute force via modular addition (adding a known
|
||||
value to the private key shifts the public key predictably). Hashing eliminates
|
||||
this shortcut—targeting a specific node_id prefix requires full brute force
|
||||
against SHA-256, making node ID grinding as expensive as the hash strength allows.
|
||||
|
||||
**Separation of concerns**: The nsec/npub keypair is used exclusively for
|
||||
cryptographic operations (signing protocol messages, identity verification,
|
||||
end-to-end encryption). The node_id derived from the npub is used only for
|
||||
routing network traffic. This separation keeps cryptographic material out of
|
||||
routing tables and packet headers.
|
||||
|
||||
### Address Format
|
||||
|
||||
FIPS addresses use the IPv6 Unique Local Address (ULA) prefix `fd00::/8`. This
|
||||
provides 120 bits for the node_id hash while avoiding conflicts with global
|
||||
unicast addresses that may be in use on underlying IPv6 transports.
|
||||
|
||||
```text
|
||||
FIPS Address (128 bits):
|
||||
┌────────┬────────────────────────────────────────────────────┐
|
||||
│ 0xfd │ node_id[0:15] │
|
||||
│ 8 bits │ 120 bits │
|
||||
└────────┴────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
FIPS addresses are overlay identifiers, not routable IPv6 addresses. They never
|
||||
appear in IPv6 headers on the underlying transport; the `fd` prefix simply ensures
|
||||
no collision with addresses that may be legitimately in use on that transport.
|
||||
|
||||
### Identity Verification
|
||||
|
||||
FIPS uses two complementary signing mechanisms:
|
||||
|
||||
**General signing** is used for protocol messages (TreeAnnounce, LookupResponse,
|
||||
etc.) where the signer is asserting authorship of data:
|
||||
|
||||
```text
|
||||
signature = schnorr_sign(nsec, SHA256(message))
|
||||
```
|
||||
|
||||
**Challenge-response authentication** is used during connection establishment
|
||||
to prove a node controls the private key corresponding to its claimed npub.
|
||||
|
||||
### Peer Authentication Protocol (Noise IK)
|
||||
|
||||
When two nodes establish a connection, they perform mutual authentication using
|
||||
the Noise Protocol Framework with the IK pattern. This provides:
|
||||
|
||||
- Mutual authentication (both parties prove identity)
|
||||
- Forward secrecy (ephemeral keys protect past sessions)
|
||||
- Encrypted link (all subsequent messages are encrypted)
|
||||
|
||||
> **Terminology note**: *Peer authentication* (this section) is hop-by-hop—it
|
||||
> verifies that a direct peer is who they claim to be and establishes an
|
||||
> encrypted link. This is distinct from *session-layer encryption* which
|
||||
> provides end-to-end authenticated encryption between FIPS addresses. Both
|
||||
> layers operate independently: peer auth secures each link; session encryption
|
||||
> secures the full path.
|
||||
|
||||
#### Protocol: Noise_IK_secp256k1_ChaChaPoly_SHA256
|
||||
|
||||
The IK pattern is chosen because:
|
||||
|
||||
- **Outbound connections**: Initiator knows responder's static key from config
|
||||
- **Inbound connections**: Responder learns initiator's identity from message 1
|
||||
|
||||
```text
|
||||
Pre-message (known before handshake):
|
||||
<- s (responder's static key known to initiator)
|
||||
|
||||
Handshake:
|
||||
-> e, es, s, ss (msg1: initiator sends ephemeral + encrypted static)
|
||||
<- e, ee, se (msg2: responder sends ephemeral)
|
||||
```
|
||||
|
||||
```text
|
||||
Initiator (A) Responder (B)
|
||||
│ │
|
||||
│ knows B's npub from config │
|
||||
│ │
|
||||
│───── msg1 (82 bytes) ──────────────────────►│
|
||||
│ e: ephemeral pubkey (33) │
|
||||
│ s: encrypted static (33+16) │
|
||||
│ │ learns A's identity
|
||||
│◄───── msg2 (33 bytes) ──────────────────────│
|
||||
│ e: ephemeral pubkey (33) │
|
||||
│ │
|
||||
▼ ▼
|
||||
Noise session established Noise session established
|
||||
(symmetric keys derived) (symmetric keys derived)
|
||||
```
|
||||
|
||||
#### Cryptographic Primitives
|
||||
|
||||
| Component | Choice | Notes |
|
||||
|-----------|---------------------|---------------------------|
|
||||
| Curve | secp256k1 | Nostr-native |
|
||||
| DH | ECDH on secp256k1 | Standard EC Diffie-Hellman|
|
||||
| AEAD | ChaCha20-Poly1305 | Same as NIP-44 |
|
||||
| Hash | SHA-256 | Nostr-native |
|
||||
| KDF | HKDF-SHA256 | Standard Noise KDF |
|
||||
|
||||
#### Crossing Connection Handling
|
||||
|
||||
When both nodes simultaneously initiate connections, a deterministic tie-breaker
|
||||
resolves which connection survives:
|
||||
|
||||
```text
|
||||
Rule: Smaller node_id's OUTBOUND connection wins
|
||||
|
||||
If our_node_id < their_node_id:
|
||||
- Our outbound wins, close our inbound
|
||||
If our_node_id > their_node_id:
|
||||
- Our inbound wins, close our outbound
|
||||
```
|
||||
|
||||
Both nodes independently reach the same conclusion without coordination.
|
||||
|
||||
#### Post-Authentication State
|
||||
|
||||
After the Noise handshake completes:
|
||||
|
||||
- **NoiseSession** holds symmetric keys for encrypt/decrypt
|
||||
- Link is fully encrypted (all subsequent messages use AEAD)
|
||||
- Peer's verified identity is extracted from the handshake
|
||||
- Node transitions from `PeerConnection` to `ActivePeer` state
|
||||
|
||||
#### Link vs Session Encryption
|
||||
|
||||
FIPS uses two independent encryption layers:
|
||||
|
||||
| Layer | Scope | Keys | Purpose |
|
||||
|-------------|-------------|--------------------------|--------------------------------------|
|
||||
| **Link** | Hop-by-hop | Per-peer Noise session | Encrypt all traffic on this link |
|
||||
| **Session** | End-to-end | Per-destination session | Encrypt payload across multiple hops |
|
||||
|
||||
A packet from A→D through B traverses:
|
||||
|
||||
1. A encrypts payload with A↔D session key
|
||||
2. A encrypts that with A↔B link key, sends to B
|
||||
3. B decrypts link layer, routes, re-encrypts with B↔C or B↔D link key
|
||||
4. D decrypts link layer, then decrypts session layer to get payload
|
||||
|
||||
Intermediate nodes can route but cannot read session-layer payloads.
|
||||
|
||||
---
|
||||
|
||||
## 2. Spanning Tree Protocol
|
||||
|
||||
### Background
|
||||
|
||||
A spanning tree is a subgraph of a mesh network that includes all nodes but
|
||||
contains no cycles. It has the following properties:
|
||||
|
||||
- **Unique paths**: Exactly one path exists between any two nodes
|
||||
- **N-1 edges**: A spanning tree with N nodes has exactly N-1 edges
|
||||
- **Minimal connectivity**: Removing any edge disconnects the tree
|
||||
|
||||
A *minimum* spanning tree optimizes for some metric across all edges—typically
|
||||
minimizing total cost, latency, or hop count. In FIPS, parent selection considers
|
||||
link quality metrics, causing the tree to approximate a minimum spanning tree
|
||||
with respect to those metrics.
|
||||
|
||||
In a distributed system, nodes construct a spanning tree by each selecting a
|
||||
single parent. The result is a rooted tree where every node can reach every
|
||||
other node by traversing toward their lowest common ancestor.
|
||||
|
||||
### Purpose
|
||||
|
||||
The spanning tree provides the routing backbone for FIPS. Unlike traditional
|
||||
routing protocols that require global routing tables or centralized coordination,
|
||||
the spanning tree creates a distributed structure that enables:
|
||||
|
||||
- **Destination lookup**: Finding the current location of any node by its node_id
|
||||
- **Greedy forwarding**: Routing packets toward their destination without source routes
|
||||
- **Multicast scope**: Limiting lookup broadcasts to relevant subtrees via bloom filters
|
||||
|
||||
### Design Criteria
|
||||
|
||||
1. **Minimal state**: Each node maintains only its parent selection and immediate
|
||||
peer information, not global topology
|
||||
2. **Rapid convergence**: Topology changes propagate quickly through gossip
|
||||
3. **Partition tolerance**: Isolated network segments form independent trees that
|
||||
merge when connectivity is restored
|
||||
4. **Transport-aware**: Parent selection considers link quality, not just reachability
|
||||
5. **Byzantine tolerance**: Malicious nodes cannot claim arbitrary tree positions
|
||||
without valid signatures
|
||||
|
||||
### Relationship to Yggdrasil
|
||||
|
||||
The spanning tree protocol is based on concepts proven in Yggdrasil v0.5 / Ironwood.
|
||||
Deviations from that design are noted where applicable.
|
||||
|
||||
### Tree State
|
||||
|
||||
The spanning tree is maintained as a distributed data structure with CRDT
|
||||
(Conflict-free Replicated Data Type) semantics. This provides eventual consistency
|
||||
without requiring coordination: nodes can make local decisions about parent
|
||||
selection, gossip updates to peers, and the system converges to a consistent
|
||||
global view.
|
||||
|
||||
Each node selects exactly one parent (or itself if it believes it is root) and
|
||||
has zero or more peers (direct connections over any transport). Through gossip,
|
||||
each node learns about other nodes' parent selections, building a local view of
|
||||
the tree.
|
||||
|
||||
Each peer's TreeAnnounce message includes its full ancestry—the chain of parent
|
||||
selections from that peer up to the root. This means a node's TreeState contains:
|
||||
|
||||
- **Direct peers**: Their parent selections received directly
|
||||
- **Ancestors of peers**: Every node on the path from each peer to the root
|
||||
|
||||
This ancestry information is essential for computing tree coordinates and the
|
||||
distance metric used in greedy routing.
|
||||
|
||||
```text
|
||||
TreeState = {
|
||||
(node_id, parent_id, sequence, signature, timestamp),
|
||||
(node_id, parent_id, sequence, signature, timestamp),
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**Generating announcements**: A node generates a new TreeAnnounce when:
|
||||
|
||||
- It selects a new parent (including initial startup)
|
||||
- A periodic refresh interval expires (to maintain liveness)
|
||||
- It detects its parent has become unreachable
|
||||
|
||||
Each announcement contains the node's current parent selection, an incremented
|
||||
sequence number, a timestamp, and a Schnorr signature over these fields. The
|
||||
announcement also includes the node's full ancestry—the chain of parent
|
||||
declarations from itself up to the current root.
|
||||
|
||||
**Processing received announcements**: When a node receives a TreeAnnounce from
|
||||
a peer, it:
|
||||
|
||||
1. Verifies the signature on the sender's parent declaration
|
||||
2. Verifies signatures on each entry in the ancestry chain
|
||||
3. Validates that the ancestry forms a coherent path to a valid root
|
||||
4. Merges each entry into its local TreeState
|
||||
|
||||
**Merge rules**: When merging an entry for a given node_id:
|
||||
|
||||
- Higher sequence number always wins
|
||||
- On sequence tie, prefer the entry with the later timestamp
|
||||
- On both tie, prefer lexicographically smaller parent_id (deterministic)
|
||||
- Entries not refreshed within the TTL are expired and removed
|
||||
|
||||
These rules ensure all nodes converge to the same TreeState view despite
|
||||
receiving updates in different orders.
|
||||
|
||||
### Root Election
|
||||
|
||||
The root of the spanning tree is the node with the lexicographically smallest
|
||||
node_id among all nodes a given node can reach. This election is deterministic
|
||||
and requires no explicit coordination—each node independently arrives at the
|
||||
same conclusion from its local TreeState.
|
||||
|
||||
**Startup behavior**: A newly joined node initially considers itself the root
|
||||
(parent = self). As it receives TreeAnnounce messages from peers, it discovers
|
||||
nodes with smaller node_ids and adopts a new parent whose ancestry leads to
|
||||
the smallest known node_id.
|
||||
|
||||
**Partition behavior**: If the network partitions, each isolated segment elects
|
||||
its own root (the smallest node_id within that segment). When partitions merge,
|
||||
nodes in the segment with the larger root discover the globally smaller root
|
||||
and re-parent accordingly. The tree reconverges automatically.
|
||||
|
||||
### Parent Selection
|
||||
|
||||
Each node selects a parent that provides the best path to the current root,
|
||||
considering both reachability and link quality. The parent must be a direct
|
||||
peer—nodes cannot select non-peers as parents.
|
||||
|
||||
**Stability mechanism**: To prevent flapping during minor topology changes, a
|
||||
node only changes its parent if the improvement exceeds a threshold. This
|
||||
hysteresis ensures the tree remains stable under transient conditions.
|
||||
|
||||
**Selection criteria**:
|
||||
|
||||
1. The candidate parent must have a path to the current root
|
||||
2. Among valid candidates, prefer the one with lowest effective cost
|
||||
3. Only switch if the improvement exceeds the stability threshold
|
||||
|
||||
### Tree Coordinates
|
||||
|
||||
A node's coordinate is its path to root:
|
||||
|
||||
```text
|
||||
Coordinate = [self_id, parent_id, ..., root_id]
|
||||
```
|
||||
|
||||
Coordinates are ordered self-to-root, so common ancestry is a suffix. This
|
||||
ordering is consistent across all FIPS documents.
|
||||
|
||||
**Distance metric**: Tree distance between two nodes is the sum of hops to their
|
||||
lowest common ancestor (LCA). With self-to-root ordering, the LCA is found by
|
||||
comparing coordinate suffixes:
|
||||
|
||||
```
|
||||
dist(A, B) = depth(A) + depth(B) - 2 * depth(LCA(A, B))
|
||||
```
|
||||
|
||||
### Gossip Efficiency
|
||||
|
||||
Two strategies reduce gossip bandwidth:
|
||||
|
||||
**Delta encoding**: A node tracks the last sequence number sent to each peer for
|
||||
each ancestor. Subsequent announcements omit entries that haven't changed since
|
||||
the last transmission to that peer. This optimization is always beneficial—it
|
||||
reduces bandwidth without affecting convergence.
|
||||
|
||||
**Partial ancestry**: On severely constrained links, a node may send only its
|
||||
immediate parent declaration, relying on transitive propagation through other
|
||||
gossip paths to eventually deliver the full ancestry.
|
||||
|
||||
The tradeoff with partial ancestry is convergence speed. With full ancestry, the
|
||||
recipient immediately knows the sender's complete tree coordinate, can compute
|
||||
accurate distances, and can route packets right away. With parent-only, the
|
||||
recipient must wait for the remaining ancestors to propagate through other paths
|
||||
before routing works correctly. On high-bandwidth links, the extra bytes for full
|
||||
ancestry are cheap and provide immediate usability. On a 300 bps radio link,
|
||||
accepting slower convergence may be necessary to avoid transmitting a long chain
|
||||
of ancestor entries.
|
||||
|
||||
### Cost Metrics
|
||||
|
||||
Parent selection and routing decisions depend on link cost metrics. The primary
|
||||
metrics are:
|
||||
|
||||
- **Latency**: Round-trip time for the link
|
||||
- **Packet loss**: Proportion of packets that fail to arrive
|
||||
- **Bandwidth**: Available throughput capacity
|
||||
|
||||
These metrics combine into an effective cost used for parent selection and
|
||||
routing decisions. The specific formula for combining metrics, the measurement
|
||||
methodology, and the weighting of each factor are areas for future specification.
|
||||
|
||||
---
|
||||
|
||||
## 3. Bloom Filter Routing
|
||||
|
||||
### Yggdrasil Design
|
||||
|
||||
- 8192-bit bloom filter (1024 bytes)
|
||||
- 8 hash functions per key
|
||||
- False positive rate ~1/million for 200-node subtree
|
||||
- Saturates in network core (acts as default route)
|
||||
|
||||
### Lookup Protocol
|
||||
|
||||
When a node needs to reach an unknown destination:
|
||||
|
||||
1. Create lookup packet with destination key
|
||||
2. Forward to on-tree peers whose bloom filter contains the key
|
||||
3. If multiple matches, send to all (multicast)
|
||||
4. Destination responds with its current coordinates
|
||||
5. Sender caches coordinate for direct routing
|
||||
|
||||
### FIPS Adaptations
|
||||
|
||||
**Tunable filter size**: Different deployments may need different tradeoffs:
|
||||
|
||||
| Scenario | Filter Size | Hash Functions | Target Nodes |
|
||||
|----------|-------------|----------------|--------------|
|
||||
| Small mesh (<100) | 2048 bits | 4 | 50 |
|
||||
| Medium network | 8192 bits | 8 | 500 |
|
||||
| Large network | 32768 bits | 12 | 2000 |
|
||||
|
||||
**Filter compression**: For low-bandwidth links (radio), use:
|
||||
|
||||
- Compressed bloom filter representation
|
||||
- Hierarchical filters (subnet then node)
|
||||
- Lazy propagation with invalidation
|
||||
|
||||
**Key transformation**: Allow filtering on npub prefixes for subnet routing:
|
||||
|
||||
```
|
||||
filter.add(SHA256(npub)[0:8]) // 64-bit prefix for subnet
|
||||
filter.add(SHA256(npub)) // Full key for node
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Greedy Routing
|
||||
|
||||
### Algorithm
|
||||
|
||||
```
|
||||
route(packet, destination):
|
||||
if destination == self:
|
||||
deliver(packet)
|
||||
return
|
||||
|
||||
best_peer = None
|
||||
best_distance = tree_distance(self, destination)
|
||||
|
||||
for peer in connected_peers:
|
||||
d = tree_distance(peer, destination)
|
||||
if d < best_distance:
|
||||
best_distance = d
|
||||
best_peer = peer
|
||||
|
||||
if best_peer:
|
||||
forward(packet, best_peer)
|
||||
else:
|
||||
send_path_broken(packet.source)
|
||||
```
|
||||
|
||||
### Path-Broken Recovery
|
||||
|
||||
When greedy routing fails (local minimum):
|
||||
|
||||
1. Send path-broken notification back to source
|
||||
2. Source initiates bloom filter lookup for destination
|
||||
3. On response, source caches new coordinates
|
||||
4. Retry with updated routing information
|
||||
|
||||
---
|
||||
|
||||
## 5. Transport Abstraction Layer
|
||||
|
||||
FIPS is transport-agnostic. Transports are the physical or logical interfaces
|
||||
over which FIPS communicates (UDP sockets, Ethernet NICs, Tor clients, etc.).
|
||||
Links are connection instances to specific peers over a transport.
|
||||
|
||||
> **Note**: The Transport trait definition and detailed transport specifications
|
||||
> are in [fips-architecture.md](fips-architecture.md). This section provides a
|
||||
> conceptual overview. See [fips-transports.md](fips-transports.md) for transport
|
||||
> characteristics and requirements.
|
||||
|
||||
### Transport Interface Concept
|
||||
|
||||
Each transport driver provides:
|
||||
|
||||
- **Identity**: Transport type identifier and configuration
|
||||
- **Lifecycle**: Start/stop the transport interface
|
||||
- **I/O**: Send/receive datagrams to/from transport-layer addresses
|
||||
- **Discovery**: Find potential peers (transport-specific mechanism)
|
||||
- **MTU**: Maximum packet size for this transport
|
||||
|
||||
Transports handle framing, fragmentation, and any transport-layer encryption
|
||||
internally. The FIPS routing layer sees only FIPS packets.
|
||||
|
||||
### Transport Types
|
||||
|
||||
#### TCP/TLS Transport
|
||||
|
||||
Standard Yggdrasil-style IP peering with TLS encryption.
|
||||
|
||||
#### QUIC Transport
|
||||
|
||||
UDP-based with built-in encryption and multiplexing.
|
||||
|
||||
#### Radio Transport (LoRa, HF, VHF/UHF)
|
||||
|
||||
- Packet-based with size limits
|
||||
- May support broadcast
|
||||
- Often asymmetric (different TX/RX capabilities)
|
||||
- Requires careful bandwidth management
|
||||
|
||||
#### Serial Transport (RS-232, USB, etc.)
|
||||
|
||||
- Point-to-point
|
||||
- Framing protocol needed (SLIP, HDLC, etc.)
|
||||
- Good for isolated node pairs
|
||||
|
||||
#### Onion Transport (Tor, I2P)
|
||||
|
||||
- High latency
|
||||
- Strong anonymity properties
|
||||
- Special handling for circuit setup
|
||||
|
||||
#### Bluetooth/BLE Transport
|
||||
|
||||
- Short range
|
||||
- Discovery via scanning
|
||||
- Pairing considerations
|
||||
|
||||
### Multi-Transport Routing
|
||||
|
||||
A single node may have multiple transports of different types:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ FIPS Node │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ Router Core │ │
|
||||
│ └──────────┬──────────┬───────────┘ │
|
||||
│ │ │ │
|
||||
│ ┌──────┴────┐ ┌───┴─────┐ │
|
||||
│ │ TCP │ │ LoRa │ │
|
||||
│ │ Transport │ │Transport│ │
|
||||
│ └────┬──────┘ └────┬────┘ │
|
||||
└───────────┼─────────────┼──────────────┘
|
||||
│ │
|
||||
┌────┴────┐ ┌─────┴────┐
|
||||
│Internet │ │ Radio │
|
||||
│ Peers │ │ Peers │
|
||||
└─────────┘ └──────────┘
|
||||
```
|
||||
|
||||
**Transport selection for forwarding**:
|
||||
|
||||
- Prefer transport with best path to destination
|
||||
- Consider transport characteristics (don't send bulk over LoRa)
|
||||
- Support explicit transport preferences in routing hints
|
||||
|
||||
---
|
||||
|
||||
## 6. Protocol Messages
|
||||
|
||||
FIPS uses a discriminator-based wire format with session indices for efficient
|
||||
dispatch. See [fips-wire-protocol.md](fips-wire-protocol.md) for complete
|
||||
wire format specification, security properties, and dispatch logic.
|
||||
|
||||
### Wire Format
|
||||
|
||||
All FIPS link-layer packets begin with a 1-byte discriminator:
|
||||
|
||||
```text
|
||||
┌─────────────┬────────────────────────────────────────────────┐
|
||||
│ Discriminator│ Type-Specific Payload │
|
||||
│ 1 byte │ Variable │
|
||||
└─────────────┴────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
| Byte | Type | Payload Format |
|
||||
|------|-----------------|--------------------------------------------------|
|
||||
| 0x00 | Encrypted frame | `[receiver_idx:4][counter:8][ciphertext+tag]` |
|
||||
| 0x01 | Noise IK msg1 | `[sender_idx:4][noise_msg1:82]` |
|
||||
| 0x02 | Noise IK msg2 | `[sender_idx:4][receiver_idx:4][noise_msg2:33]` |
|
||||
|
||||
**Session indices** enable O(1) dispatch without relying on source address,
|
||||
supporting transport-layer roaming. Each party allocates a random 32-bit index
|
||||
during handshake; packets include the receiver's index for fast session lookup.
|
||||
|
||||
### Handshake Messages
|
||||
|
||||
Exchanged during Noise IK handshake before link encryption is established.
|
||||
|
||||
| Type | Name | Size | Description |
|
||||
|------|-------------|----------|-------------------------------------------------|
|
||||
| 0x01 | NoiseIKMsg1 | 87 bytes | Initiator: index + ephemeral + encrypted static |
|
||||
| 0x02 | NoiseIKMsg2 | 42 bytes | Responder: indices + ephemeral pubkey |
|
||||
|
||||
### Encrypted Frames
|
||||
|
||||
Post-handshake packets use the 0x00 discriminator with AEAD encryption:
|
||||
|
||||
- **receiver_idx**: Identifies session for O(1) lookup (no trial decryption)
|
||||
- **counter**: 64-bit monotonic nonce, also used for replay detection
|
||||
- **ciphertext**: ChaCha20-Poly1305 encrypted payload with 16-byte tag
|
||||
|
||||
The plaintext begins with a message type byte (see Link Layer Messages below).
|
||||
|
||||
### Link Layer Messages (0x10-0x4F)
|
||||
|
||||
Exchanged between directly connected peers over Noise-encrypted links.
|
||||
All payloads are encrypted with session keys from the Noise IK handshake.
|
||||
|
||||
| Type | Name | Description |
|
||||
|------|----------------|--------------------------------------------|
|
||||
| 0x10 | TreeAnnounce | Spanning tree state announcement |
|
||||
| 0x20 | FilterAnnounce | Bloom filter reachability update |
|
||||
| 0x30 | LookupRequest | Request to discover node coordinates |
|
||||
| 0x31 | LookupResponse | Response with target's coordinates |
|
||||
| 0x40 | SessionDatagram| Encapsulated session-layer payload |
|
||||
|
||||
### Session Layer Messages (End-to-End)
|
||||
|
||||
Carried inside `SessionDatagram`, encrypted with end-to-end session keys.
|
||||
Intermediate nodes route based on destination but cannot read the payload.
|
||||
|
||||
| Type | Name | Description |
|
||||
|------|----------------|--------------------------------------------|
|
||||
| 0x00 | SessionSetup | Session establishment with coordinates |
|
||||
| 0x01 | SessionAck | Session acknowledgement |
|
||||
| 0x10 | DataPacket | Encrypted IPv6 datagram |
|
||||
| 0x20 | CoordsRequired | Router cache miss notification |
|
||||
| 0x21 | PathBroken | Route failure notification |
|
||||
|
||||
See [fips-routing.md](fips-routing.md) Part 4 for routing session details.
|
||||
|
||||
### TreeAnnounce
|
||||
|
||||
```
|
||||
TreeAnnounce {
|
||||
sender: [u8; 32], // npub
|
||||
sequence: u64,
|
||||
parent: [u8; 32], // parent npub (self if root)
|
||||
ancestry_count: u8,
|
||||
ancestry: [(pubkey, seq, sig), ...],
|
||||
signature: [u8; 64], // Schnorr signature
|
||||
}
|
||||
```
|
||||
|
||||
### BloomUpdate
|
||||
|
||||
```
|
||||
BloomUpdate {
|
||||
sender: [u8; 32],
|
||||
link_id: u32, // Which peer link this filter is for
|
||||
filter_size: u16, // In bits
|
||||
filter: [u8; ...], // Bloom filter bytes
|
||||
sequence: u64,
|
||||
}
|
||||
```
|
||||
|
||||
### Lookup / LookupResponse
|
||||
|
||||
```
|
||||
Lookup {
|
||||
source: [u8; 32],
|
||||
destination: [u8; 32],
|
||||
ttl: u8,
|
||||
nonce: [u8; 16],
|
||||
}
|
||||
|
||||
LookupResponse {
|
||||
destination: [u8; 32],
|
||||
nonce: [u8; 16], // Echo from request
|
||||
coordinates: Vec<[u8; 32]>, // Path to root [self, parent, ..., root]
|
||||
signature: [u8; 64],
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Security Considerations
|
||||
|
||||
### Threat Model
|
||||
|
||||
- **Passive adversary**: Can observe traffic on controlled links
|
||||
- **Active adversary**: Can inject, modify, or drop packets
|
||||
- **Sybil attacks**: Can create many identities
|
||||
|
||||
### Mitigations
|
||||
|
||||
**Signature verification**: All protocol messages signed by sender's nsec.
|
||||
|
||||
**Replay protection**: Sequence numbers and timestamps on tree announcements.
|
||||
|
||||
**Sybil resistance**:
|
||||
|
||||
- Tree coordinate verification (can't claim arbitrary position)
|
||||
- Optional proof-of-work for identity registration
|
||||
- Web-of-trust integration with Nostr follows graph
|
||||
|
||||
**Traffic analysis**:
|
||||
|
||||
- Padding options for fixed-size packets
|
||||
- Chaff traffic on idle links
|
||||
- Onion routing mode for sensitive traffic
|
||||
|
||||
### Encryption
|
||||
|
||||
**Link encryption**: Each link type provides its own encryption:
|
||||
|
||||
- TCP: TLS 1.3
|
||||
- QUIC: Built-in TLS
|
||||
- Radio: Pre-shared key or public-key encryption
|
||||
|
||||
**End-to-end encryption**: FIPS provides a crypto session layer using the Noise
|
||||
Protocol Framework with secp256k1. The Noise KK pattern provides mutual
|
||||
authentication and forward secrecy in a single round-trip, since both parties
|
||||
know each other's npub before initiating. Session keys are used with
|
||||
ChaCha20-Poly1305 AEAD for all data packets; no per-packet signatures are
|
||||
required (AEAD tag provides integrity and authenticity). See
|
||||
[fips-session-protocol.md](fips-session-protocol.md) §6 for crypto session details.
|
||||
|
||||
> **Note**: Applications may use additional encryption (NIP-44) for
|
||||
> application-layer privacy, but FIPS-layer encryption protects against
|
||||
> intermediate router observation.
|
||||
|
||||
---
|
||||
|
||||
## 8. Open Questions
|
||||
|
||||
1. **Root stability**: How to prevent root flapping in large networks?
|
||||
Yggdrasil uses cost thresholds, but this may need tuning for heterogeneous links.
|
||||
|
||||
2. **Multi-path routing**: Should FIPS support simultaneous paths through different
|
||||
link types? Useful for redundancy and bandwidth aggregation.
|
||||
|
||||
3. **Bloom filter propagation on slow links**: How to handle 1KB filter updates
|
||||
over 300 bps radio links? Differential updates? Hierarchical filters?
|
||||
|
||||
4. **NAT traversal**: Yggdrasil relies on TCP for NAT punch-through. How do other
|
||||
transports handle this? (Not applicable to radio/serial)
|
||||
|
||||
5. **Incentives**: Should there be any incentive mechanism for relaying traffic?
|
||||
Or rely on reciprocal altruism?
|
||||
|
||||
6. **Nostr relay integration**: Can FIPS nodes announce themselves via Nostr relays?
|
||||
Use kind 10002-style relay lists for FIPS peer discovery?
|
||||
|
||||
7. **IPv6 integration**: Should FIPS addresses be routable IPv6, or use a private
|
||||
range with translation at gateways?
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
### FIPS Design Documents
|
||||
|
||||
- [fips-session-protocol.md](fips-session-protocol.md) — Traffic flow, session terminology, crypto sessions
|
||||
- [fips-routing.md](fips-routing.md) — Bloom filters, discovery, routing sessions
|
||||
- [fips-architecture.md](fips-architecture.md) — Software architecture, configuration
|
||||
- [fips-transports.md](fips-transports.md) — Transport protocol characteristics
|
||||
- [spanning-tree-dynamics.md](spanning-tree-dynamics.md) — Tree protocol dynamics
|
||||
|
||||
### External References
|
||||
|
||||
- [Yggdrasil Network](https://yggdrasil-network.github.io/)
|
||||
- [Yggdrasil v0.5 Release Notes](https://yggdrasil-network.github.io/2023/10/22/upcoming-v05-release.html)
|
||||
- [Ironwood Routing Library](https://github.com/Arceliar/ironwood)
|
||||
- [Nostr Protocol](https://github.com/nostr-protocol/nips)
|
||||
- [NIP-44 Encryption](https://github.com/nostr-protocol/nips/blob/master/44.md)
|
||||
@@ -0,0 +1,546 @@
|
||||
# FIPS: Federated Interoperable Peering System
|
||||
|
||||
## What is FIPS?
|
||||
|
||||
FIPS is a self-organizing mesh network that can operate over any transport
|
||||
medium—radio, 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
|
||||
datagram service, or through an IPv6 adaptation layer that presents each node
|
||||
as an IPv6 endpoint for compatibility with existing IP-based applications.
|
||||
|
||||
## Why FIPS?
|
||||
|
||||
**Infrastructure independence**: The internet depends on centralized
|
||||
infrastructure—ISPs, 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
|
||||
internet is unavailable, unreliable, or untrusted, the mesh still works.
|
||||
|
||||
**End-to-end security**: FIPS provides secure, authenticated, and encrypted
|
||||
communication between any two nodes in the mesh, independent of the mix of
|
||||
transports used along the routed path between them.
|
||||
|
||||
**Privacy by design**: Traffic flows through encrypted tunnels at every hop.
|
||||
Intermediate nodes route packets but cannot read their contents. Metadata
|
||||
exposure is limited to direct peers only.
|
||||
|
||||
**Zero configuration**: Nodes discover each other and build routing
|
||||
automatically. Connect to one peer and you can reach the entire mesh. The
|
||||
network self-heals around failures and adapts to changing topology.
|
||||
|
||||
**Self-sovereign identity**: FIPS nodes generate their own addresses, node IDs,
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Nodes with multiple transports automatically bridge between networks—the same
|
||||
routing logic works regardless of the underlying transport mix.
|
||||
|
||||
## 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
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Application Layer │
|
||||
│ (native FIPS API, or IPv6 via TUN adapter) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ FIPS Router │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ Identity │ │ Spanning │ │ Bloom Filter │ │
|
||||
│ │ (npub) │ │ Tree │ │ Routing Table │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Transport Abstraction │
|
||||
│ ┌────────┐ ┌──────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
|
||||
│ │ UDP │ │ Ethernet │ │ WiFi │ │ Radio │ │ Onion │ │
|
||||
│ └────────┘ └──────────┘ └────────┘ └────────┘ └────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
See [fips-transports.md](fips-transports.md) for transport options and characteristics.
|
||||
|
||||
## 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 KK pattern for 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.
|
||||
|
||||
---
|
||||
|
||||
## Identity System
|
||||
|
||||
FIPS uses Nostr keypairs (secp256k1) as node identities. The public key (npub)
|
||||
identifies the node; the private key (nsec) signs protocol messages and
|
||||
establishes encrypted sessions.
|
||||
|
||||
### Node ID Derivation
|
||||
|
||||
The npub is hashed to derive a node_id used for routing:
|
||||
|
||||
```
|
||||
npub (secp256k1 x-only pubkey, 32 bytes)
|
||||
→ SHA-256
|
||||
→ node_id (32 bytes)
|
||||
→ truncate with prefix
|
||||
→ FIPS address (128 bits, fd::/8)
|
||||
```
|
||||
|
||||
**Why hash the npub?** The node_id is the only identifier used at the protocol
|
||||
level; the associated npub cannot be derived from it. This one-way derivation
|
||||
also prevents "grinding" attacks—secp256k1 public keys can be shifted to achieve
|
||||
specific prefixes via modular addition, but targeting a node_id prefix requires
|
||||
full brute force against SHA-256.
|
||||
|
||||
**Separation of concerns**: The nsec/npub keypair handles cryptographic
|
||||
operations (signing, encryption). The node_id derived from the npub handles
|
||||
routing. This keeps cryptographic material out of routing tables and packet
|
||||
headers.
|
||||
|
||||
### Address Format
|
||||
|
||||
When using the IPv6 protocol adapter, FIPS addresses use the IPv6 Unique Local
|
||||
Address (ULA) prefix `fd00::/8`, providing 120 bits from the node_id hash.
|
||||
These are overlay identifiers—they 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.
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Two-Layer Encryption
|
||||
|
||||
FIPS uses independent encryption at two layers:
|
||||
|
||||
| Layer | Scope | Pattern | Purpose |
|
||||
|-------------|-------------|----------|--------------------------------------|
|
||||
| **Link** | Hop-by-hop | Noise IK | Encrypt all traffic on each link |
|
||||
| **Session** | End-to-end | Noise KK | Encrypt payload across multiple hops |
|
||||
|
||||
### Link Layer (Peer-to-Peer)
|
||||
|
||||
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 traffic 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
|
||||
the first handshake message.
|
||||
|
||||
### Session Layer (End-to-End)
|
||||
|
||||
For traffic between non-adjacent nodes, FIPS establishes end-to-end encrypted
|
||||
sessions using Noise KK. Both parties know each other's npub before initiating,
|
||||
enabling mutual authentication in a single round-trip.
|
||||
|
||||
A packet from A to D through intermediate node B:
|
||||
|
||||
1. A encrypts payload with A↔D session key
|
||||
2. A encrypts that with A↔B link key, sends to B
|
||||
3. B decrypts link layer, sees 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
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Spanning Tree Protocol
|
||||
|
||||
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.
|
||||
|
||||
### Why a Spanning Tree?
|
||||
|
||||
A spanning tree has useful properties:
|
||||
|
||||
- **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
|
||||
|
||||
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.
|
||||
|
||||
### Tree Coordinates
|
||||
|
||||
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.
|
||||
|
||||
### Root Election
|
||||
|
||||
The root is the node with the lexicographically smallest node_id 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.
|
||||
|
||||
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).
|
||||
|
||||
### Parent Selection
|
||||
|
||||
Each node selects a parent that provides the best path to root, considering:
|
||||
|
||||
- Reachability (the parent must have a path to root)
|
||||
- Link quality (latency, packet loss, bandwidth)
|
||||
- Stability (hysteresis prevents flapping on minor changes)
|
||||
|
||||
The parent must be a direct peer—nodes cannot select non-peers as parents.
|
||||
|
||||
### Tree Gossip
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
### Partition Handling
|
||||
|
||||
If the network partitions, each isolated segment elects its own root (the
|
||||
smallest node_id 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.
|
||||
|
||||
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_ids 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_id?
|
||||
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_ids 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.
|
||||
|
||||
---
|
||||
|
||||
## Transport Abstraction
|
||||
|
||||
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.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ FIPS Node │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ Router Core │ │
|
||||
│ └──────────┬──────────┬───────────┘ │
|
||||
│ │ │ │
|
||||
│ ┌──────┴────┐ ┌───┴─────┐ │
|
||||
│ │ UDP │ │ LoRa │ │
|
||||
│ │ Transport │ │Transport│ │
|
||||
│ └────┬──────┘ └────┬────┘ │
|
||||
└───────────┼─────────────┼──────────────┘
|
||||
│ │
|
||||
┌────┴────┐ ┌─────┴────┐
|
||||
│Internet │ │ Radio │
|
||||
│ Peers │ │ Peers │
|
||||
└─────────┘ └──────────┘
|
||||
```
|
||||
|
||||
### Multi-Transport Bridging
|
||||
|
||||
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 |
|
||||
| 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.
|
||||
|
||||
See [fips-transports.md](fips-transports.md) for transport characteristics.
|
||||
|
||||
---
|
||||
|
||||
## 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) |
|
||||
| 0x11 | FilterAnnounce | Bloom filter reachability update |
|
||||
| 0x12 | LookupRequest | Query for node's tree coordinates |
|
||||
| 0x13 | LookupResponse | Response with coordinates and proof |
|
||||
| 0x40 | SessionDatagram| Carries end-to-end encrypted payloads |
|
||||
|
||||
### Session Layer Messages
|
||||
|
||||
Carried inside SessionDatagram, encrypted end-to-end 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) |
|
||||
| 0x20 | CoordsRequired | Router cache miss—need fresh coordinates |
|
||||
| 0x21 | PathBroken | Greedy routing failed—need re-lookup |
|
||||
|
||||
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
|
||||
|
||||
### Threat Model
|
||||
|
||||
FIPS assumes adversaries with varying capabilities:
|
||||
|
||||
- **Passive adversary**: Can observe traffic on links they control
|
||||
- **Active adversary**: Can inject, modify, drop, or replay packets
|
||||
- **Sybil adversary**: Can create many node identities
|
||||
|
||||
### Cryptographic Protections
|
||||
|
||||
**Link encryption**: Every peer connection uses Noise IK, providing mutual
|
||||
authentication and forward secrecy. An observer on the underlying transport
|
||||
sees only encrypted packets.
|
||||
|
||||
**End-to-end encryption**: Session-layer Noise KK 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.
|
||||
|
||||
**Replay protection**: Sequence numbers and timestamps on announcements.
|
||||
Counter-based nonces with sliding window for encrypted packets.
|
||||
|
||||
### 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.
|
||||
- **Tree coordinate verification**: Nodes cannot claim arbitrary tree positions
|
||||
without valid signed ancestry chains from real nodes
|
||||
- **Rate limiting**: Handshake rate limiting constrains how fast attackers can
|
||||
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_ids, packet size |
|
||||
| Destination | Your npub (identity), payload content |
|
||||
|
||||
Intermediate routers see node_ids, not npubs. Since node_ids are derived from
|
||||
npubs 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
|
||||
|
||||
FIPS combines these elements into a cohesive system that achieves its design
|
||||
goals:
|
||||
|
||||
- **Self-sovereign identity** through Nostr keypairs, with node_ids 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
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
### FIPS Design Documents
|
||||
|
||||
| Document | Description |
|
||||
|----------|-------------|
|
||||
| [fips-session-protocol.md](fips-session-protocol.md) | End-to-end session flow, Noise KK 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-architecture.md](fips-architecture.md) | Software architecture, configuration |
|
||||
|
||||
### External References
|
||||
|
||||
- [Yggdrasil Network](https://yggdrasil-network.github.io/)
|
||||
- [Yggdrasil v0.5 Release Notes](https://yggdrasil-network.github.io/2023/10/22/upcoming-v05-release.html)
|
||||
- [Ironwood Routing Library](https://github.com/Arceliar/ironwood)
|
||||
- [Nostr Protocol](https://github.com/nostr-protocol/nips)
|
||||
- [Noise Protocol Framework](https://noiseprotocol.org/)
|
||||
@@ -570,7 +570,7 @@ When nodes join/leave:
|
||||
|
||||
## References
|
||||
|
||||
- [fips-design.md](fips-design.md) — Overall FIPS architecture
|
||||
- [fips-intro.md](fips-intro.md) — Overall FIPS architecture
|
||||
- [fips-gossip-protocol.md](fips-gossip-protocol.md) — Wire formats for TreeAnnounce, FilterAnnounce, Lookup
|
||||
- [fips-session-protocol.md](fips-session-protocol.md) — Traffic flow, crypto sessions, terminology
|
||||
- [fips-wire-protocol.md](fips-wire-protocol.md) — Link-layer transport and Noise IK handshake
|
||||
|
||||
@@ -80,7 +80,7 @@ Total overhead: 29 bytes (1 + 4 + 8 + 16)
|
||||
- **tag**: 16-byte Poly1305 authentication tag.
|
||||
|
||||
The plaintext inside the encrypted frame begins with a message type byte,
|
||||
followed by the message-specific payload (see fips-design.md §6 for message
|
||||
followed by the message-specific payload (see fips-intro.md for message
|
||||
types 0x10-0x4F).
|
||||
|
||||
### 2.2 Noise IK Message 1 (0x01)
|
||||
@@ -790,7 +790,7 @@ protocol layers apply additional policy.
|
||||
|
||||
### Internal Documents
|
||||
|
||||
- [fips-design.md](fips-design.md) - Overall protocol design
|
||||
- [fips-intro.md](fips-intro.md) - Overall protocol design
|
||||
- [fips-session-protocol.md](fips-session-protocol.md) - Session establishment flow
|
||||
- [fips-architecture.md](fips-architecture.md) - Software architecture
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A detailed study of the gossip-based spanning tree protocol, focusing on
|
||||
operational behavior under various mesh conditions. This document complements
|
||||
[fips-design.md](fips-design.md) with step-by-step walkthroughs of protocol
|
||||
[fips-intro.md](fips-intro.md) with step-by-step walkthroughs of protocol
|
||||
dynamics rather than message formats and data structures.
|
||||
|
||||
For wire formats, see [fips-gossip-protocol.md](fips-gossip-protocol.md) §2 (TreeAnnounce).
|
||||
|
||||
Reference in New Issue
Block a user