Nostr Agent Onboarding · start here · index · source:
nostr-dev/docs/sources/fips.md· snapshot 2026-10-10Paths such as
~/Documents/…,~/Production Environment/…,repos/…and services onlocalhostrefer to the author's workstation and are not available to you — read them as worked examples of a setup you can recreate.
FIPS — Free Internetworking Peering System
Spec home: https://fips.network
Source: https://github.com/jmcorgan/fips (Rust, MIT)
Local clone: ~/Documents/nostr-dev/repos/fips/ (offline reference, gitignored from this repo)
Local build: binaries in ~/Documents/nostr-dev/repos/fips/target/release/ and copied to ~/.cargo/bin/{fips,fipsctl,fipstop,fips-gateway} after cargo install.
Why it lives in the workspace docs
FIPS is the cleanest worked example of the workspace's central design idea
(see docs/design-synthesis.md §1) applied outside Nostr's own
event/relay surface. Where Nostr decouples authentication from event
storage, FIPS decouples it from packet routing — it uses the same
secp256k1 keypair, the same npub identity space, and the same Nostr-relay
infrastructure for peer discovery, then runs an encrypted mesh on top of
arbitrary transports.
If you're designing anything that wants "Nostr keypair as identity" but
isn't itself a Nostr application, FIPS is the most fully-developed
reference architecture available. The 13 design specs in
repos/fips/docs/design/ are worth reading on their own as a layered
protocol design exercise.
What FIPS is, in one paragraph
A distributed mesh-routing protocol where each node is identified by a
Nostr secp256k1 keypair. Nodes self-organize into a spanning tree across
arbitrary transports (UDP, TCP, Ethernet, Bluetooth, Tor, serial),
forwarding packets hop-by-hop with bloom-filter-aided routing. Every
packet gets dual encryption: hop-by-hop (Noise IK) at the link layer
and end-to-end (Noise XK) at the session layer. An IPv6 adapter maps
npubs to fd00::/8 ULA addresses and serves a .fips DNS namespace, so
unmodified IP applications (curl, ssh, a Nostr relay, anything) work
over the mesh without modification.
Layered design
| Layer | Protocol | Job |
|---|---|---|
| Transport | platform-specific (UDP / TCP / Ethernet / BLE / Tor / serial) | Uniform datagram interface over heterogeneous media |
| Mesh (FMP) | Noise IK + spanning tree | Peer authentication, hop-by-hop link encryption, forwarding |
| Session (FSP) | Noise XK | End-to-end encryption between any two nodes, session lifecycle |
| IPv6 adapter | TUN + DNS resolver | Maps npubs ↔ fd00::/8 addresses; .fips DNS for app interop |
Each layer's spec is one markdown file in repos/fips/docs/design/.
The Nostr integration — kinds and tags
FIPS uses three Nostr kinds in production. All three are interesting examples for docs/kind-cheatsheet.md because two of them are in the wild but not (yet) in the canonical NIPs registry.
Kind 37195 — overlay advert (addressable)
The digits visually spell "FIPS" — 7=F, 1=I, 9=P, 5=S. (Cute.)
A node publishes its currently-reachable transport endpoints as a kind 37195 event signed by its FIPS identity key (= its Nostr identity key). Peers that know the npub fetch the advert from the node's configured relays and append the advertised endpoints to their dial list.
- Class: addressable (range 30000–39999). Latest event per
(pubkey, kind, d-tag)is what matters. dtag: application namespace, defaultfips-overlay-v1. Lets multiple FIPS deployments coexist on the same relays without crosstalk.- Tag content: transport endpoints (
udp:host:port,tcp:host:port,tor:onion:port, the specialudp:natrendezvous token), TTLs, capabilities. - Unpublication: standard NIP-9 kind:5 delete, addressed at the kind 37195 event id.
This is a textbook case of "addressable kind in the wild, not in the registry." Per the kind-cheatsheet's Step 2, anyone considering kind 37195 for their own use should nak req -k 37195 -l 3 wss://relay.damus.io wss://nos.lol and discover that someone is already there. Coordinate via the FIPS project rather than collide.
Kind 21059 — encrypted offer/answer (ephemeral, gift-wrapped)
For NAT hole-punching. When two nodes are both behind UDP NAT, they exchange STUN-derived candidate pairs through Nostr using NIP-59 gift-wrap. The outer wrap is kind 1059 (NIP-59 standard); the inner sealed event is kind 21059 (FIPS-specific, ephemeral 20000–29999 range).
- Class: ephemeral. The event has no value once the punch attempt completes.
- Routing: to the recipient's NIP-65 inbox relays (with fallback to
dm_relaysfrom the FIPS YAML config).
Another good real-world example — ephemeral kinds are uncommon outside NIP-46 / NIP-42 / NIP-98 (the core auth-flow kinds), so seeing one for application-specific live signaling is instructive.
Kind 10002 — relay list (replaceable, NIP-65)
FIPS reuses NIP-65 unmodified for "where do my offer/answer messages get delivered." A FIPS node looking up a peer for hole-punch first fetches the peer's kind 10002 event, then publishes the kind 21059 gift-wrap to those inbox relays.
This is the right move — invent kinds only when an existing one doesn't fit.
Binaries
| Binary | What |
|---|---|
fips |
Main daemon: TUN adapter, transports, mesh + session protocols. Needs root for TUN. |
fipsctl |
CLI: query node status, peers, links, sessions, metrics. Doesn't need root. |
fipstop |
TUI dashboard, like htop for a FIPS node. |
fips-gateway |
Optional daemon: lets unmodified LAN hosts reach mesh destinations via DNS-allocated virtual IPs and kernel NAT. |
In this workspace they're built at repos/fips/target/release/ and installed to ~/.cargo/bin/. The agent has them on PATH but no daemon is running.
Why no fips daemon is running on this machine
By design choice — same posture as the other "potentially-internet-facing service" decisions in this workspace:
fipsrequires root to load the TUN driver. Different threat model than localhost-bound containers.- A FIPS node's value depends on a persistent identity (npub), since its routing state is anchored to that key. The workspace's
/dev/shmephemeral key would invalidate adverts on every reboot — pointless for a real mesh node. - Running a node binds UDP 2121 and TCP 8443 outbound by default and joins a live mesh. Operationally meaningful; not appropriate as a default.
- The
nostr-discoveryfeature is off by default in stock configs; an operator opts in deliberately. We respect that default.
If you want to run one, repos/fips/packaging/ has Debian, AUR, OpenWrt, macOS, and Windows packagers. The minimal operator-facing flow:
# Install the .deb (or use the cargo-built binaries)
sudo cp ~/Documents/nostr-dev/repos/fips/target/release/fips /usr/local/bin/
sudo mkdir -p /etc/fips && sudo cp <your-config>.yaml /etc/fips/fips.yaml
# (config needs a persistent identity — generate or supply an nsec)
sudo /usr/local/bin/fips
# In another terminal:
fipsctl show status
fipstop
Don't do this with the workspace's ephemeral session key.
Spec docs (offline at repos/fips/docs/design/)
| File | Topic |
|---|---|
fips-intro.md |
Goals, layered architecture, problem statement |
fips-transport-layer.md |
Datagram interface over arbitrary media |
fips-mesh-layer.md |
FMP — peer auth, link encryption, forwarding |
fips-session-layer.md |
FSP — end-to-end encryption, session lifecycle |
fips-ipv6-adapter.md |
TUN + DNS adapter mapping npubs ↔ fd00::/8 |
fips-mesh-operation.md |
Routing, discovery, error recovery |
fips-wire-formats.md |
Message wire formats |
fips-nostr-discovery.md |
The Nostr integration — read this if you only read one |
fips-spanning-tree.md |
Root discovery, parent selection, coordinates |
fips-bloom-filters.md |
FPR analysis, size classes, split-horizon |
fips-configuration.md |
YAML configuration reference |
fips-gateway.md |
LAN gateway adapter |
spanning-tree-dynamics.md |
Walkthroughs of convergence scenarios |
How this informs the workspace's design knowledge
- §1 of
design-synthesis.md(the "single load-bearing idea") — FIPS is the worked example showing that "signatures decouple authentication from storage" generalizes beyond event databases to packet routing. kind-cheatsheet.md— kind 37195 is a real-world reminder to probenak req -k <N> ... wss://relay.damus.io wss://nos.lolbefore allocating any kind, because the registry is incomplete.patterns/grasp-as-structural-layer.md— different pattern but same underlying lesson: Nostr identity is the anchor; the structural layer is the part you choose. FIPS chose "decentralized routing"; the archive project chose "Git tree."