Files
fips/docs/design
ArjenandJohnathan Corgan 2787062436 fix(lookup): accept our own lookup response instead of relaying it away
An originator could misfile the answer to its own lookup as transit, so
discovery reported "requests went unanswered" while the replies were in
fact arriving and being reverse-path forwarded to a peer.

A request is flooded to every tree peer whose bloom filter claims the
target. At a high fill ratio a false positive sends a copy out into the
wider network, which can circulate it back to the originator. On arrival
`classify_request` applied its only identity test, `target == my_addr`,
which a lookup we originated never satisfies, so the copy was recorded in
`recent_requests` as an ordinary transit entry keyed on our own
`request_id`. When the target answered, `classify_response` consulted
`recent_requests` first, matched that entry, and relayed our own answer to
the peer that looped the request. The pending lookup was never satisfied,
the ladder retried with a fresh id, and the same race repeated.

The `None` arm's reasoning — "not a request we transited, so it claims to
answer one of ours" — held only while our own ids stayed out of the dedup
cache, and nothing enforced that. `resp_unsolicited` pinned at zero while
`resp_received == resp_forwarded` was the tell: control never reached the
originator arm. Affected nodes showed `resp_accepted` at 1 of 291.

It is a race, not a hard failure: when the genuine reply beats the looped
copy the cache is still clean and the lookup succeeds. It bites hardest at
the junction between a quiet subtree and a dense public network, where a
lookup for a descendant goes both correctly downward and outward through a
false positive, and the wide network returns it first.

Two changes close it, on both sides of the invariant:

- `classify_response` tests the pending lookups before `recent_requests`.
  A response naming a target with a lookup outstanding, carrying an id
  that lookup issued, is ours whatever the dedup cache holds. The id is
  fresh 64-bit randomness per attempt and the target signs over it, so an
  id we never issued still cannot match and the replay properties the
  `Unsolicited` arm protects are unchanged.

- `classify_request` drops a request whose id a pending lookup for that
  target issued, rather than recording it. Our own id never enters the
  transit cache, so a duplicate reply cannot be misrouted after we accept
  the first, and the returning copy is not forwarded a second time. The
  guard keys on the id, so another node's lookup for the same target still
  transits normally.

Verified against the reported deployment: discovery now completes where it
previously exhausted the four-attempt ladder.
2026-08-31 19:35:10 +00:00
..
2026-08-30 10:42:59 +00:00
2026-08-30 10:42:59 +00:00

FIPS Design

Architectural and protocol-level explanations for FIPS — the why and the how behind the wire and the system. For wire formats and configuration keys, see reference/. For task recipes, see how-to/. For end-to-end lessons, see tutorials/.

Reading Order

Start with fips-concepts.md for the novice-friendly framing of what FIPS is and why, then move to fips-architecture.md for the protocol stack, identity model, and two-layer encryption walkthrough. From there, follow the protocol stack from bottom to top. After the stack, fips-mesh-operation.md explains how the pieces work together at runtime. Cross-cutting and supporting documents cover specific subsystems in detail.

Foundations

Document Description
fips-concepts.md What FIPS is, why it exists, mental model
fips-architecture.md Protocol stack, identity, two-layer encryption
fips-prior-work.md Designs and protocols FIPS builds on

Protocol Stack

Document Description
fips-transport-layer.md Transport layer: datagram delivery over arbitrary media
fips-mesh-layer.md FIPS Mesh Protocol (FMP): peer authentication, link encryption, forwarding
fips-session-layer.md FIPS Session Protocol (FSP): end-to-end encryption, sessions
fips-ipv6-adapter.md IPv6 adaptation: TUN interface, DNS, MTU enforcement
fips-native-api.md Native datagram API: pubkey-addressed flows over FSP, and what it is instead of the TUN path

Cross-Cutting

Document Description
fips-mmp.md Metrics Measurement Protocol (link + session)
fips-mtu.md Path MTU model, encapsulation overhead, PMTUD
fips-security.md fips0 interface threat model and default-deny baseline

Mesh Behavior

Document Description
fips-mesh-operation.md How the mesh operates: routing, discovery, error recovery
fips-nostr-discovery.md Optional Nostr-mediated peer discovery and UDP NAT hole-punch
port-advertisement-and-nat-traversal.md Nostr-signaled port advertisement and UDP NAT-traversal protocol; generic, with FIPS as an example implementation

Deeper Dives

Document Description
fips-spanning-tree.md Spanning tree algorithms: root discovery, parent selection, coordinates
fips-bloom-filters.md Bloom filter properties: FPR analysis, size classes, split-horizon
spanning-tree-dynamics.md Spanning tree walkthroughs: convergence scenarios, worked examples

Adjacent Components

Document Description
fips-gateway.md fips-gateway service: outbound (LAN-to-mesh) DNS-proxy + virtual-IP NAT and inbound (mesh-to-LAN) port-forwarding, sharing one nftables table