Files
fips/docs
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 14:39:52 +00:00
2026-08-30 10:42:59 +00:00

FIPS Documentation

FIPS (Free Internetworking Peering System) is a self-organizing encrypted mesh network built on Nostr identities, capable of operating over arbitrary transports — local networks, the public internet, Tor, Bluetooth, or point-to-point links — without central infrastructure.

With FIPS, your machine becomes a node in the mesh with a self-generated cryptographic identity. There are two ways to deploy it.

As an overlay on top of existing IP networks, FIPS lets your node reach any other FIPS node wherever it sits — behind a NAT, on a different ISP, on a phone over cellular, on a laptop with only Bluetooth in range, or behind a Tor onion. The mesh forwards IPv6 traffic transparently and end-to-end encrypted, with no central VPN concentrator or coordinating server.

From the ground up over raw Ethernet, WiFi, or Bluetooth, FIPS provides a complete permissionless network without any pre-existing IP infrastructure, ISP, or DNS. Any node that joins the link gets routable IPv6 addresses, peer discovery, and a path to every other node automatically.

Either way, existing networking software runs over it unchanged: SSH, HTTP servers, file transfer, anything IPv6-native works the same way it would on a local network.

New to FIPS? Start with the Getting Started guide.

Documentation Sections

Tutorials

If you are starting from scratch and want a guided path to a working mesh, go here.

How-To Guides

If you have a specific task in mind — enabling a feature, deploying a component, diagnosing a problem — go here.

Reference

If you need to look up wire formats, configuration keys, command flags, or counter inventories, go here.

Design

If you want to understand how the mesh self-organizes, why FIPS makes the choices it does, or how the pieces fit together, go here.

Releases

If you want the notes for a particular version — what changed, what broke, and what to do about it on upgrade — go here.