Restructures /docs/ by reader purpose (tutorials, how-to, reference, design), adds the new-user-progression and operator-recipe content the prior layout lacked, runs an accuracy pass against current source across the pre-existing design docs, and rewrites the gateway feature-set documentation end-to-end around its actual operational profile (a niche feature designed for systems already serving DHCP/DNS to a LAN, with two independent halves — outbound LAN→mesh, inbound mesh→LAN — sharing one nftables table, one binary, and one control socket). Top-level README and getting-started rewritten around two equally-weighted deployment modes (overlay on existing IP networks; ground-up over non-IP transports). ## Additions - 11 new tutorials in docs/tutorials/: an 8-step new-user progression from single-daemon test-mesh peering through to a ground-up two-device mesh, an IPv6-adapter side-trip walkthrough, an Advanced Tutorials index, and a hand-held OpenWrt walk-through for fips-gateway deployment that exercises both halves of the feature. - 12 new how-tos in docs/how-to/: firewall activation, Nostr discovery (resolve / advertise / open across five scenarios), Tor onion (directory + control_port modes), UDP buffer tuning, unprivileged-user setup, persistent identity, host aliases, Bluetooth LE peering, MTU diagnostics, manual Linux-host gateway deployment (covers both halves), gateway troubleshooting (organised by half), and a section index. - 9 new reference docs in docs/reference/: configuration, wire formats, control-socket protocol, four CLI references (fips, fipsctl, fipstop, fips-gateway), security posture matrix, and Nostr events catalog. Configuration and wire-formats are renamed-and-extended from prior design/ versions; the other seven are net-new. - 6 new design docs: fips-concepts, fips-architecture, and fips-prior-work split out of the deleted fips-intro.md; consolidated fips-mmp and fips-mtu aggregations; and a new generic port-advertisement-and-nat-traversal doc (Nostr-signaled port advertisement plus UDP NAT-traversal protocol, FIPS as an example implementation, suitable for eventual NIP submission). - Top-level docs/getting-started.md walking through the binary-installer-only Install story. - packaging/common/hosts pre-populated with the eight public test-mesh nodes so shortnames resolve out of the box on every fresh install. ## Changes - 23 wire-format diagrams relocated to reference/diagrams/ alongside the wire-formats move. - 4 design diagrams corrected against source code (fips-protocol-stack, fips-identity-derivation, fips-coordinate-discovery, fips-routing-decision). - 10 pre-existing design docs reconciled with current source. Numeric corrections: stale link-MMP report bounds (now [1s, 5s] with 200 ms cold-start floor); UDP default MTU (now 1280, IPv6 minimum); node_addr formula (SHA-256(pubkey)[..16]); Noise patterns (IK at link, XK at session); peer-ACL semantics (strict allowlist requires ALL in peers.deny); daemon DNS upstream ([::1]:5354); on-the-wire bloom-filter size (1,071 bytes); obsolete Cargo-feature references (PR #79 dropped them) removed. - Transport framing tightened across the docs: TCP is for UDP-filtered networks (not NAT traversal); Tor is a deployment mode (not failover); WebSocket dropped (not a shipped FIPS transport); WiFi promoted to Implemented via Ethernet in infrastructure mode; classic-Bluetooth row removed (BLE is the only Bluetooth-mode transport). - docs/design/fips-gateway.md rewritten end-to-end to lead with the niche-feature framing and the two-halves structure. Title moved from "FIPS Outbound LAN Gateway" to "FIPS Gateway"; architecture section describes the common machinery (the fips-gateway service, the nftables table, the control socket) before splitting into separate "Outbound Half" and "Inbound Half" sections of equal weight; security considerations split per-half; no Future Work section (speculative directions live in the project tracker, not in protocol design docs). Inbound port forwarding is a first-class half rather than a buried "Implemented Extensions" subsection. - Gateway terminology unified across all gateway docs as a separate Linux service running alongside the fips daemon (its own systemd unit / OpenWrt init script). Container- pattern terms (sidecar) are reserved for the Docker/Kubernetes sidecar deployment examples — the testing/sidecar/ tree, examples/k8s-sidecar/, examples/sidecar-nostr-relay/, examples/wireguard-sidecar-macos/, and the related CHANGELOG / top-level README entries — where the term carries its standard container meaning. - Net-new design body content: rekey section in fips-mesh-layer (Noise IK msg1/msg2 over the established link, K-bit cutover, drain window, smaller-NodeAddr-wins tie-breaker on dual-init); Mesh Size Estimation and Antipoison FPR Cap sections in fips-bloom-filters; Mesh-Interface Query Filter subsection in fips-ipv6-adapter; failure-suppression knobs and clock- skew tolerance in fips-nostr-discovery; loop-rejection and mid-chain ancestor swap added to spanning-tree propagation / stability rules; Priority Chain in fips-mesh-operation renumbered to match the routing-decision diagram. - Top-level README: dropped the stale nostr-discovery cargo-feature parenthetical. docs/README.md and the four section READMEs (tutorials, how-to, reference, design) refreshed for the new structure; index rows reflect both halves of the gateway feature and the new fips-gateway CLI reference. - Cargo.toml [package.metadata.deb] assets path updated for the fips-security.md move; .gitignore /reference/ rule anchored to repo root so docs/reference/ is trackable. - packaging/openwrt-ipk/files/etc/fips/fips.yaml configuration-doc URL updated to the new docs/reference/configuration.md location. ## Deletions - docs/design/fips-intro.md (split into the three new intro design docs). - docs/design/document-relationships.svg (orphan, no longer referenced). - docs/proposals/ tree removed; the only proposal it contained (the Nostr UDP hole-punch protocol) was rewritten as the new generic design/port-advertisement-and-nat-traversal.md.
8.2 KiB
Nostr Event Reference
The Nostr-protocol surface FIPS uses for discovery and signaling. For the design of the discovery runtime and the rationale behind these event shapes, see ../design/fips-nostr-discovery.md. For operator activation recipes, see ../how-to/enable-nostr-discovery.md.
FIPS uses three Nostr event kinds:
| Kind | Name | Encryption | Storage | Purpose |
|---|---|---|---|---|
| 37195 | Overlay advert | None (signed only) | Replaceable | Publish reachable transport endpoints |
| 21059 | Traversal signaling | NIP-44 inside NIP-59 gift wrap | Ephemeral | Carry TraversalOffer/TraversalAnswer payloads |
| 10050 | NIP-17 inbox relay list | None (signed only) | Replaceable | Tell dialers where to publish offers |
All three are signed with the node's FIPS identity key (the same secp256k1 keypair Nostr uses); there is no separate Nostr key.
Kind 37195 — Overlay Advert
A parameterized replaceable event in the application-defined
replaceable range 30000–39999 (the digits visually spell FIPS:
7=F, 1=I, 9=P, 5=S). Each node has a single in-place-updatable advert
under its identity.
Tags
d— fixed to the literalfips-overlay-v1(the application identifier baked into the binary). Together withpubkey, this identifies the unique replaceable event slot.protocol— the configurednode.discovery.nostr.appvalue (defaultfips-overlay-v1). Distinct from thedtag so the application string can evolve without breaking the replaceable event slot.version— protocol version string (currently"1").expiration— NIP-40 expiration timestamp set to now +node.discovery.nostr.advert_ttl_secs(default 3600 seconds). Conforming relays stop serving the event after this time.
Content
The event content is a JSON document shaped as OverlayAdvert:
{
"identifier": "fips-overlay-v1",
"version": 1,
"endpoints": [
{"transport": "udp", "addr": "203.0.113.45:2121"},
{"transport": "tor", "addr": "xxxxx.onion:8443"},
{"transport": "udp", "addr": "nat"}
],
"signalRelays": ["wss://relay.damus.io", "wss://nos.lol"],
"stunServers": ["stun:stun.l.google.com:19302"]
}
Field semantics:
| Field | Type | Description |
|---|---|---|
identifier |
string | Application namespace; must match the d tag. |
version |
integer | Advert schema version (currently 1). |
endpoints |
array | List of transport endpoints. Each is {transport, addr} where transport is "udp", "tcp", or "tor", and addr is "host:port", ".onion:port", or the literal "nat" (for UDP NAT-punch). |
signalRelays |
array? | Optional. Relays the publisher prefers for offer/answer signaling. Present only when at least one endpoint is udp:nat. |
stunServers |
array? | Optional. STUN servers the publisher uses for reflexive discovery. Present only when at least one endpoint is udp:nat. Informational — peers do not use these to choose their own STUN targets. |
Signature scope
The Nostr event signature covers the standard Nostr event ID
(serialized [0, pubkey, created_at, kind, tags, content]), so the
content JSON, tags, kind, and timestamp are all bound to the signing
identity.
Replacement and deletion
Because kind 37195 is replaceable, publishing a new advert replaces
the prior one in the same (pubkey, d-tag) slot. To withdraw an
advert without publishing a successor, the node publishes a NIP-9
kind 5 delete event referencing the prior advert.
Kind 21059 — Traversal Signaling
An ephemeral event (kinds in the 20000–29999 range are not stored by
conforming relays). Used to deliver gift-wrapped, NIP-44-encrypted
TraversalOffer and TraversalAnswer payloads between dialer and
responder during a UDP NAT hole-punch.
Encryption envelope
The wire shape is the standard NIP-59 gift wrap:
- Rumor — the unsigned
TraversalOffer/TraversalAnswerpayload (JSON), authored by the actual sender's identity. - Seal — a kind 13 event whose content is the rumor NIP-44-encrypted to the recipient's pubkey, signed by the sender.
- Gift wrap — a kind 21059 event whose content is the seal
NIP-44-encrypted to the recipient under an ephemeral key, signed
by that ephemeral key. The outer
pubkeyof the kind 21059 event is the ephemeral identity, not the sender's real identity.
Only the intended recipient can decrypt the wrap to recover the seal, and only the recipient can decrypt the seal to recover the rumor.
Wrapped payloads
The TraversalOffer carries:
type— message-type tag.sessionId— unique identifier correlating offer and answer.senderNpub/recipientNpub— bech32-encoded pubkeys, repeated inside the encrypted payload (the outer wrap pubkey is ephemeral).issuedAt/expiresAt— Unix-ms timestamps;expiresAtisissuedAt + signal_ttl_secs * 1000.nonce— random per-offer value.reflexiveAddress—{protocol, ip, port}observed via STUN, ornullif STUN failed or returned no usable address.localAddresses— array of{protocol, ip, port}private candidates, populated whenshare_local_candidatesis enabled.stunServer— the STUN server actually used (informational).
The TraversalAnswer echoes sessionId and carries:
type,senderNpub,recipientNpub,issuedAt,expiresAt,nonce— same shape as the offer.inReplyTo— the offer's event id.accepted— boolean; false when the responder has no usable addresses.reflexiveAddressandlocalAddresses— the responder's candidates, in the same shape as the offer.stunServer— informational.punch— aPunchHint { startAtMs, intervalMs, durationMs }telling both sides when to begin probing and how aggressively. Absent on rejected offers.reason— optional rejection string whenacceptedis false.offerReceivedAt— optional responder wall-clock (Unix ms) at the moment it received the offer; the initiator uses this to derive a clock-skew estimate.
Relay selection
Dialer publishes offers to the recipient's NIP-17 inbox relays (kind
10050) when available; otherwise to the local
node.discovery.nostr.dm_relays list. The responder publishes the
answer back through the same relay channel.
Kind 10050 — NIP-17 Inbox Relay List
A standard NIP-17 event used by FIPS to advertise which relays this node prefers for receiving direct-message-style signaling — for FIPS, the gift-wrapped traversal offers (kind 21059).
This is NIP-17 (kind 10050, inbox relays for DM delivery), not
NIP-65 (kind 10002, general read/write relay list). The two serve
different purposes:
- Kind 10002 (NIP-65) — general read/write relays for ordinary event publication and subscription.
- Kind 10050 (NIP-17) — relays the recipient prefers for receiving DM-shaped (NIP-59 wrapped) events.
FIPS publishes its own kind 10050 on startup so dialers can discover
where to send traversal offers. When dialing a peer, FIPS first
fetches the peer's kind 10050 from the peer's advert_relays; on
fetch failure it falls back to the local dm_relays list.
Tags
Standard NIP-17 form: each relay is encoded as an r tag whose
single value is the relay URL.
["r", "wss://relay.damus.io"]
["r", "wss://nos.lol"]
Content
Empty per NIP-17.
See also
- ../design/fips-nostr-discovery.md — discovery runtime design and the five activation scenarios
- ../how-to/enable-nostr-discovery.md — operator recipes
- ../tutorials/resolve-peers-via-nostr.md, ../tutorials/advertise-your-node.md, ../tutorials/open-discovery.md — hand-held tutorial walkthroughs of the three capabilities
- ../design/port-advertisement-and-nat-traversal.md — generic protocol reference (event tags, NIP usage, on-the-wire offer/answer schema), with FIPS values as worked examples
- security.md — how the FIPS identity key signs both adverts and Noise handshakes