mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
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.
339 lines
13 KiB
Markdown
339 lines
13 KiB
Markdown
# Open Discovery: Find Peers Without Configuration
|
|
|
|
After
|
|
[advertise-your-node](advertise-your-node.md), your daemon
|
|
publishes its endpoint on Nostr and other open-discovery nodes
|
|
on the test mesh have already started dialing you. This
|
|
tutorial flips the symmetry: turn your own daemon into a
|
|
consumer of every advert in the namespace, so any operator
|
|
who's publishing becomes a candidate peer of yours.
|
|
|
|
The whole exercise should take about ten minutes. After it,
|
|
you'll be a full participant in the ambient
|
|
`fips-overlay-v1` namespace — publishing your own advert
|
|
*and* discovering everyone else's.
|
|
|
|
## What you'll build
|
|
|
|
```text
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ Nostr relays — fips-overlay-v1 namespace │
|
|
│ adverts from: test-us01..uk01, others │
|
|
└────────────────┬─────────────────────────▲──────────┘
|
|
│ │
|
|
│ subscribe to all │ your own
|
|
│ adverts in namespace │ advert
|
|
│ │
|
|
▼ │
|
|
┌────────────────────────────────────────────────────┐
|
|
│ your fips daemon (policy: open) │
|
|
│ │
|
|
│ peers list grows ambient as adverts arrive: │
|
|
│ test-us01 ← was static │
|
|
│ test-us03 ← inbound (was already there) │
|
|
│ test-de01, test-es01, test-uk01, test-us04... │
|
|
│ plus any other publisher in the namespace │
|
|
└────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
You will change one thing in `/etc/fips/fips.yaml`: under
|
|
`discovery.nostr`, set `policy: open` (the default is
|
|
`configured_only`). After restart, the daemon subscribes to
|
|
every Kind 37195 advert in the `fips-overlay-v1` namespace and
|
|
queues the publishers for outbound connection attempts.
|
|
|
|
## How open discovery works
|
|
|
|
> **Discovery policy.** `discovery.nostr.policy` decides what
|
|
> the daemon does with incoming advert data. Two values:
|
|
>
|
|
> - `configured_only` (the default): the daemon only consumes
|
|
> adverts for peers it has explicitly listed with
|
|
> `via_nostr: true`. This is what you've been running
|
|
> through the previous two Nostr tutorials.
|
|
> - `open`: the daemon subscribes to every advert in the
|
|
> configured `app` namespace. Any publisher becomes a
|
|
> candidate peer, no `peers:` list entry needed.
|
|
|
|
Switching the policy doesn't disturb anything that was already
|
|
working:
|
|
|
|
> **Open is additive, not exclusive.** Switching to
|
|
> `policy: open` doesn't replace your static peers — both
|
|
> mechanisms run in parallel. Configured peers stay in your
|
|
> `peers:` block and continue to be dialed via their static
|
|
> addresses or `via_nostr` lookups; open-discovered peers
|
|
> stack on top from the ambient namespace. You can run open
|
|
> discovery with a populated `peers:` list (the path this
|
|
> tutorial walks, since you're keeping `test-us01`), with
|
|
> `peers: []` for pure ambient discovery, or with a long
|
|
> `peers:` list and open layered on top to broaden reach.
|
|
|
|
The namespace is what scopes who's visible to whom:
|
|
|
|
> **The namespace is the scope.** `discovery.nostr.app`
|
|
> defaults to `fips-overlay-v1` — the namespace the public
|
|
> test mesh uses. Setting a different value (e.g.,
|
|
> `app: "my-experiment.v1"`) carves out a private discovery
|
|
> set: only nodes that share your `app` value find each
|
|
> other. For this tutorial we stay on the default and join
|
|
> the public namespace.
|
|
|
|
Open discovery is best-effort by design — not every
|
|
discovered peer will connect:
|
|
|
|
> **Best-effort, by design.** Many discovered peers will fail
|
|
> to connect — they may be offline, behind incompatible NAT,
|
|
> running a different protocol version, or have peer ACLs
|
|
> that reject you. That's normal for ambient discovery;
|
|
> connection attempts are best-effort and rate-limited by
|
|
> `open_discovery_max_pending` (default 64). Your peer list
|
|
> grows over time as candidates land in the cache, not all
|
|
> at once on restart.
|
|
|
|
The peer ACL is the admission-control surface, separate from
|
|
discovery:
|
|
|
|
> **Open is admission-free under your peer ACL.** Open
|
|
> discovery does not bypass the peer ACL — every candidate
|
|
> still has to pass it. By default the ACL accepts everyone,
|
|
> so any publisher in the namespace becomes a connection
|
|
> candidate. If you rely on a non-default ACL for admission
|
|
> control, verify it is set the way you want *before*
|
|
> enabling `policy: open`. See
|
|
> [../reference/security.md](../reference/security.md) for
|
|
> the ACL format.
|
|
|
|
## Step 1: Confirm your starting state
|
|
|
|
You should be coming out of
|
|
[advertise-your-node](advertise-your-node.md) with:
|
|
|
|
- Persistent identity, advertising enabled
|
|
(`discovery.nostr.advertise: true`), UDP advertising on
|
|
Nostr (`transports.udp.advertise_on_nostr: true`).
|
|
- A static `test-us01` peer entry that the daemon dials
|
|
outbound; possibly an inbound `test-us03` peer (the
|
|
open-discovery test mesh node that dialed in after seeing
|
|
your advert).
|
|
|
|
Capture the current peer count for comparison:
|
|
|
|
```sh
|
|
sudo fipsctl show peers | grep -c 'npub'
|
|
```
|
|
|
|
You'll likely see 1 (just `test-us01`) up to a handful, depending
|
|
on how many open-discovery test mesh nodes have already dialed
|
|
you.
|
|
|
|
## Step 2: Switch the discovery policy to `open`
|
|
|
|
Open `/etc/fips/fips.yaml` and find the `discovery.nostr`
|
|
block. Add (or change) the `policy` line:
|
|
|
|
```yaml
|
|
node:
|
|
identity:
|
|
persistent: true
|
|
discovery:
|
|
nostr:
|
|
enabled: true
|
|
advertise: true
|
|
policy: open
|
|
```
|
|
|
|
That's the only change. Notes on what you don't have to touch:
|
|
|
|
- **You don't have to drop the `peers:` block.** Static peers
|
|
and open-discovered peers coexist; static entries get
|
|
priority for direct dialing, open-discovered ones are
|
|
layered on top.
|
|
- **You don't have to set `app`.** The default
|
|
`fips-overlay-v1` is the namespace the public test mesh
|
|
uses; staying with the default is what gets you discovered.
|
|
- **You don't have to set `open_discovery_max_pending`.** The
|
|
default of 64 is plenty for a tutorial; only tune it if you
|
|
see the daemon log
|
|
`open-discovery: max-pending reached, deferring`.
|
|
|
|
Save the file.
|
|
|
|
## Step 3: Restart and let discovery populate
|
|
|
|
```sh
|
|
sudo systemctl restart fips
|
|
```
|
|
|
|
Give the daemon a minute or two. Open discovery doesn't fire
|
|
all at once — the daemon subscribes to the relays, accumulates
|
|
adverts as they arrive (or as relays return historical
|
|
events), and queues each publisher for a dial attempt.
|
|
|
|
## Step 4: Inspect the discovered peer list
|
|
|
|
```sh
|
|
sudo fipsctl show peers
|
|
```
|
|
|
|
You should see considerably more entries than before:
|
|
|
|
- `test-us01` — still there, still using the configured
|
|
static dial path.
|
|
- `test-us03` — same as before (the open-discovery test
|
|
mesh node that dials you when it sees your advert).
|
|
- `test-us04`, `test-de01`, `test-es01`, `test-uk01` — the
|
|
other test mesh nodes; your daemon picked their adverts up
|
|
from the namespace and dialed them.
|
|
- Plus any other operator publishing on `fips-overlay-v1`
|
|
(community nodes, other operators' experiments).
|
|
|
|
Each entry has its own `connectivity` state. Some will be
|
|
`active` (handshake completed). Some will show as
|
|
`connecting` and may transition to `failed` shortly after —
|
|
that's normal; the publisher might be offline, the advert
|
|
might be stale, or NAT traversal failed for that pair.
|
|
|
|
To get a list of just the active links:
|
|
|
|
```sh
|
|
sudo fipsctl show peers | jq '.peers[] | select(.connectivity == "active") | .npub'
|
|
```
|
|
|
|
The peer count will continue to drift over time as adverts
|
|
expire and new ones arrive. This is steady-state behavior,
|
|
not a transient.
|
|
|
|
## Step 5: Confirm the mesh-wide reach
|
|
|
|
You can now reach any of the discovered nodes the same way
|
|
you reached `test-us01` and `test-us02` in
|
|
[join-the-test-mesh](join-the-test-mesh.md). Pick one of the
|
|
new test mesh nodes from the peer list and ping it by its
|
|
shortname:
|
|
|
|
```sh
|
|
ping6 -c 4 test-uk01.fips
|
|
```
|
|
|
|
(`test-uk01` is the United Kingdom test node; the installer's
|
|
`/etc/fips/hosts` entry resolves it to the corresponding npub.
|
|
Substitute any active peer's shortname or full
|
|
`<npub>.fips`.)
|
|
|
|
Expect four replies. The packet path may go through your
|
|
direct link to that peer (if the open-discovery dial succeeded
|
|
and the link is up) or via a test-mesh forwarder (if the
|
|
direct link is down but the destination is still reachable
|
|
through the mesh). Either way, the npub-as-name addressing
|
|
works the same way.
|
|
|
|
## What you've learned
|
|
|
|
- **Open discovery is the consume side of full ambient
|
|
participation.** With `policy: open` plus advertising, your
|
|
daemon both publishes its advert *and* consumes everyone
|
|
else's in the namespace.
|
|
- **The namespace defines the scope.** Everyone publishing
|
|
with the same `app` value finds each other; different
|
|
namespaces are isolated discovery sets.
|
|
- **Best-effort means failure is normal.** Many discovered
|
|
peers won't actually connect; that's expected and
|
|
rate-limited by `open_discovery_max_pending`.
|
|
- **Static and open coexist.** Configured peers keep working
|
|
with their own dial paths; open-discovered peers stack on
|
|
top.
|
|
- **The peer ACL still gates everything.** Open is
|
|
admission-free relative to discovery, not relative to your
|
|
ACL — the ACL is what you'd use to restrict who can connect
|
|
if you don't want a fully open posture.
|
|
|
|
## Custom namespaces for private experiments
|
|
|
|
If you want to use FIPS open discovery for a private set of
|
|
nodes — colleagues, a workshop cohort, a specific deployment
|
|
— set a custom `app` value:
|
|
|
|
```yaml
|
|
discovery:
|
|
nostr:
|
|
enabled: true
|
|
advertise: true
|
|
policy: open
|
|
app: "my-team.experiment-1"
|
|
```
|
|
|
|
All nodes participating in the experiment use the same
|
|
`app` string. Pick something distinctive — short identifiers
|
|
risk colliding with other operators' experiments. Once your
|
|
nodes use a custom `app`, they no longer find or are found
|
|
by the public test mesh (the public mesh uses
|
|
`fips-overlay-v1`).
|
|
|
|
## Troubleshooting
|
|
|
|
If your peer list doesn't grow past the inbound peers from
|
|
the previous tutorial:
|
|
|
|
- **Wait.** Open discovery accumulates adverts; the first
|
|
pass after restart can take a couple of minutes to populate
|
|
on a new subscription.
|
|
- **Verify the namespace.** With `app:` unset, the daemon
|
|
uses `fips-overlay-v1`. If you set a custom `app:` for an
|
|
experiment, your daemon is in a different namespace than
|
|
the public test mesh and will only find peers using the
|
|
same value.
|
|
- **Check relay reachability.** Open discovery is a
|
|
subscription rather than one-shot queries — if the
|
|
WebSocket connection to the relays is failing repeatedly,
|
|
no adverts arrive. Look for relay-connection errors in
|
|
`sudo journalctl -u fips -n 200`.
|
|
- **`policy: open` typo.** YAML accepts and ignores unknown
|
|
values silently. If `fipsctl show status` (or the daemon's
|
|
startup log) shows `policy: configured_only`, the YAML
|
|
didn't parse the new value — re-check spelling and
|
|
indentation.
|
|
|
|
If too many peers are appearing and you want to dial down:
|
|
|
|
- **Lower `open_discovery_max_pending`.** Default 64; setting
|
|
it to e.g. 16 caps in-flight connection attempts. Adverts
|
|
beyond that wait in a queue.
|
|
- **Use a custom `app`.** Move to a private namespace where
|
|
only nodes you're coordinating with publish.
|
|
- **Use the peer ACL.** See
|
|
[../reference/security.md](../reference/security.md) for
|
|
the ACL format if you want explicit allow/deny rules.
|
|
|
|
## What's next
|
|
|
|
- **Reach services on other mesh nodes.**
|
|
[reach-mesh-services](reach-mesh-services.md) drives `nc`,
|
|
`traceroute6`, `curl`, and `ssh` at peers by `.fips` name —
|
|
any of the open-discovered peers in your list, or any node
|
|
you reach through them.
|
|
|
|
- **Host a service of your own.**
|
|
[host-a-service](host-a-service.md) brings up an HTTP server
|
|
addressable as `<your-npub>.fips`, bound to `fips0` so the
|
|
exposure is mesh-only, behind the mesh firewall.
|
|
|
|
- [ground-up-mesh](ground-up-mesh.md) — Bring up two devices on
|
|
a shared physical link (Ethernet, WiFi, or Bluetooth) with no
|
|
pre-existing IP infrastructure. The second deployment mode of
|
|
FIPS, a parallel to the overlay-on-internet path the
|
|
Nostr-discovery tutorials covered.
|
|
|
|
For the operator-style scenario reference covering all five
|
|
shapes of Nostr discovery side-by-side:
|
|
|
|
- [../how-to/enable-nostr-discovery.md](../how-to/enable-nostr-discovery.md)
|
|
§ Capability 3 — open discovery configuration knobs.
|
|
|
|
For the wire format and discovery design:
|
|
|
|
- [../reference/nostr-events.md](../reference/nostr-events.md)
|
|
— Kind 37195 advert format and the `app` namespace tag.
|
|
- [../design/fips-nostr-discovery.md](../design/fips-nostr-discovery.md)
|
|
— discovery runtime design, security and threat model.
|