Hybrid coordinate warmup: CoordsWarmup message and proactive fallback

Implement hybrid coordinate cache warming strategy: piggyback coords
via CP flag when they fit within transport MTU, send standalone
CoordsWarmup (0x14) message when they don't. On CoordsRequired or
PathBroken receipt, send CoordsWarmup immediately with source-side
rate limiting (default 2s per destination, configurable).

- Add CoordsWarmup = 0x14 session message type (empty body, CP flag)
- Add send_coords_warmup() following send_session_msg() pattern
- Restructure send_session_data() to send standalone warmup before
  data packet when piggybacked coords exceed MTU
- Add immediate CoordsWarmup response in handle_coords_required()
  and handle_path_broken() with per-destination rate limiting
- Add coords_response_interval_ms config (node.session)
- Add RoutingErrorRateLimiter::with_interval() constructor
- Zero transit-path changes: existing try_warm_coord_cache() handles
  CoordsWarmup identically to CP-flagged data packets
- Update design docs (session layer, wire formats, mesh operation,
  configuration)
This commit is contained in:
Johnathan Corgan
2026-02-19 15:25:30 +00:00
parent 999144f59a
commit f825fa242f
9 changed files with 324 additions and 77 deletions
+66 -27
View File
@@ -83,10 +83,12 @@ may traverse multiple hops, each with independent link encryption.
FLP signals routing failures asynchronously:
- **CoordsRequired**: A transit node lacks the destination's tree coordinates.
FSP responds by re-initiating discovery and resetting the coordinate warmup
strategy.
- **PathBroken**: Greedy routing reached a dead end. FSP responds by
re-discovering the destination's current coordinates and resetting warmup.
FSP responds by sending a standalone CoordsWarmup (0x14) message
(rate-limited), re-initiating discovery, and resetting the coordinate warmup
counter.
- **PathBroken**: Greedy routing reached a dead end. FSP responds by sending
a standalone CoordsWarmup (rate-limited), re-discovering the destination's
current coordinates, and resetting the warmup counter.
Both signals are generated by transit nodes (not the destination) and travel
back to the source inside a new SessionDatagram. They are plaintext (not
@@ -190,6 +192,7 @@ independent keys and sessions. The full Noise descriptor is
`Noise_IK_secp256k1_ChaChaPoly_SHA256`.
The IK pattern:
- **msg1** (`→ e, es, s, ss`): Initiator sends ephemeral key, encrypts static
key to responder. Four DH operations establish session keys.
- **msg2** (`← e, ee, se`): Responder sends ephemeral key. Both parties now
@@ -262,7 +265,7 @@ regardless of what packets were lost or reordered.
The same `ReplayWindow` and `decrypt_with_replay_check()` implementation is
used at both the link and session layers.
## CP (Coords Present) Warmup Strategy
## Hybrid Coordinate Warmup Strategy
Session establishment (SessionSetup/SessionAck) warms transit node coordinate
caches along the path. But coordinate caches have a finite TTL (default 300s),
@@ -270,14 +273,43 @@ and entries may be evicted under memory pressure. When a transit node's cache
entry expires, it cannot forward data packets (which carry only addresses, not
coordinates) and sends a CoordsRequired error.
FSP uses a warmup-then-reactive strategy to keep transit caches populated:
FSP uses a hybrid warmup strategy combining proactive piggybacking with
reactive standalone messages to keep transit caches populated:
### Warmup Phase
### Proactive Warmup Phase
After session establishment, the first N data packets (configurable, default 5)
include both source and destination coordinates via the CP flag in the FSP
common prefix. The coordinates appear in cleartext between the 12-byte header
and the ciphertext, allowing transit nodes to cache them without decryption.
per session attempt to piggyback source and destination coordinates via the CP
flag in the FSP common prefix. The coordinates appear in cleartext between the
12-byte header and the ciphertext, allowing transit nodes to cache them without
decryption.
If piggybacking coordinates would cause the total packet to exceed the
transport MTU, the source sends a standalone **CoordsWarmup** message (0x14)
first, followed by the data packet without the CP flag. This ensures transit
caches are warmed even when data packets are near the MTU limit.
### CoordsWarmup Message (0x14)
CoordsWarmup is an encrypted FSP message (phase 0x0) with the CP flag set and
inner msg_type 0x14. It carries cleartext source and destination coordinates
between the FSP header and the AEAD ciphertext, using the same format as
CP-flagged data packets. The inner body is empty — the message exists solely
to deliver coordinates to transit nodes.
Transit nodes extract coordinates via the existing `try_warm_coord_cache()`
code path with zero changes to the transit forwarding logic. CoordsWarmup is
indistinguishable from any other CP-flagged message at the transit layer.
Wire format:
```text
FSP header (12 bytes, AAD): ver=0, phase=0, flags=CP, counter, payload_len
Cleartext coords: src_coords + dst_coords (same encoding as CP flag)
AEAD ciphertext: inner_header(6) + Poly1305 tag(16) = 22 bytes
Total FSP payload: 12 + coords + 22
```
### Steady State
@@ -285,31 +317,32 @@ After the warmup count is reached, FSP clears the CP flag and sends minimal
data packets (12-byte header + ciphertext). Transit nodes serve from their
coordinate caches.
### Reactive Recovery
### Reactive Re-Warm
When FSP receives a CoordsRequired signal:
When FSP receives a CoordsRequired or PathBroken signal:
1. The warmup counter resets — subsequent data packets include coordinates again
2. A new LookupRequest may be initiated to rediscover the destination's
current coordinates
3. When the LookupResponse arrives for an established session, the warmup
1. A standalone CoordsWarmup message is sent immediately to re-warm transit
caches, rate-limited at one per destination per configurable interval
(default 2000ms, `node.session.coords_response_interval_ms`)
2. The warmup counter resets — subsequent data packets piggyback coordinates
again when possible (or send additional CoordsWarmup messages when the
data packet would exceed the MTU)
3. A new LookupRequest may be initiated to rediscover the destination's
current coordinates (always for PathBroken; optionally for CoordsRequired)
4. When the LookupResponse arrives for an established session, the warmup
counter resets again (handling the timing gap where warmup packets might
fire before transit caches are repopulated by discovery)
When FSP receives a PathBroken signal:
1. A LookupRequest is initiated to discover the destination's current
coordinates (which may have changed due to topology change)
2. The warmup counter resets
Both signals are rate-limited at transit nodes (100ms per destination) to
prevent storms during topology changes.
The source-side rate limiting prevents amplification: at most one standalone
CoordsWarmup response per destination per `coords_response_interval_ms`
(default 2s). This is independent of the transit-node rate limiting on error
signal generation (100ms per destination).
### Warmup State Machine
```text
┌──────────────┐
│ WARMUP │ ◄── Send first N packets with coords
│ WARMUP │ ◄── Send first N packets with coords (CP or CoordsWarmup)
└──────┬───────┘
│ N packets sent without CoordsRequired
@@ -318,8 +351,13 @@ prevent storms during topology changes.
└──────┬───────┘
│ CoordsRequired or PathBroken received
┌──────────────────────────┐
│ SEND CoordsWarmup (0x14) │ ◄── Immediate standalone warmup (rate-limited)
└──────────┬───────────────┘
┌──────────────┐
│ WARMUP │ ◄── Counter reset, send coords again
│ WARMUP │ ◄── Counter reset, piggyback coords again
└──────────────┘
```
@@ -336,6 +374,7 @@ configurable size (default 10K entries). There is no TTL — entries are evicted
only when the cache is full and space is needed for a new entry.
Cache population mechanisms:
- **DNS lookup**: The primary path. Resolving `npub1xxx...xxx.fips` derives
the IPv6 address and populates the identity cache.
- **Inbound traffic**: Authenticated sessions from other nodes populate the
@@ -454,7 +493,7 @@ MMP session metrics session=npub1tdwa...84le rtt=4.3ms loss=0.6% jitter=0.2ms go
| Session establishment (Noise IK) | **Implemented** |
| End-to-end encryption (ChaCha20-Poly1305) | **Implemented** |
| Explicit counter replay protection | **Implemented** |
| CP warmup-then-reactive | **Implemented** |
| Hybrid coordinate warmup (CP + CoordsWarmup) | **Implemented** |
| FSP wire format (prefix, AAD, inner header) | **Implemented** |
| Session-layer MMP | **Implemented** |
| Identity cache (LRU-only) | **Implemented** |