lofi-node now persists a relay election (n0 default | custom urls | disabled). Document the field in configuration.md, the init flags and status line in cli.md, the relay's role in pair-two-homes.md, and a production bring-your-own-relay section in beyond-the-lan.md: the public n0 relays are for development, an operator's iroh-relay rides the machine hosting the root node, and the per-node election travels in pairing tickets so no coordination or lofi-run infrastructure is needed.
- commit
- 4d3d585de24f108172101f386d6d710041ee4656
- parent
- 6bebb5e41db9660910ab5a2da4091a2ad2b7b9ec
- author
- Dami <dami.etoile@gmail.com>
| M | docs/ | 3 | ++- |
| M | docs/ | 29 | +++++++++++++++++++++++++++-- |
| M | docs/ | 8 | ++++++-- |
| M | docs/ | 7 | +++++++ |
| M | docs/ | 9 | +++++++++ |
@@ -18,7 +18,8 @@ What the node gives you:
1818 string; possession is transport access, revocation is one command. A provision-scoped ticket1919 additionally administers the store, with the admin secret never leaving the node.2020 - **Node-to-node replication over iroh.** Two homes converge by pairing tickets: dialed by public21- key, hole-punched, no static IPs, no cloud dependency.21+ key, hole-punched, no static IPs, no cloud dependency — down to22+ [the relay itself, which you can run](beyond-the-lan.md).2223 - **No silent degradation.** If the native transport layer can't load, the Jazz server still runs2324 LAN-only and `status()` says exactly why pairing is off.2425 @@ -4,8 +4,8 @@
44 deno.json compile task, src/native/loader.ts. -->55 66 A node on a trusted LAN needs nothing beyond the tutorial. Leaving the LAN — a VPS, a reachable home7-server, tickets that travel — adds three concerns: the address you pin into tickets, TLS, and the8-binary you actually run.7+server, tickets that travel — adds four concerns: the address you pin into tickets, TLS, the relay8+your mesh rides, and the binary you actually run.99 1010 ## Pin the public URL1111 @@ -29,6 +29,31 @@ Note what TLS does **not** change: the ticket still gates access, revocation sti
2929 and the WebSocket upgrade still rides the same URL — the browser client derives its endpoints from3030 the http(s) base.3131 32+## Bring your own relay33+34+Paired nodes hole-punch direct connections wherever possible; an35+[iroh relay](https://docs.iroh.computer/concepts/relays) assists that hole-punching and carries the36+traffic that cannot go direct. Out of the box a node uses n0-computer's **public relays**: shared by37+every iroh developer, rate-limited, no uptime guarantees. That default exists so the tutorial works38+in thirty seconds; n0 blesses it for development and testing, and a production mesh should not lean39+on it.40+41+```sh42+lofi-node init --relay https://relay.example.net43+```44+45+The relay is part of what you self-host, and it fits the machine you already have: `iroh-relay` is a46+single open-source binary with built-in Let's Encrypt support, cheap to run because it only carries47+the non-direct residue of your own mesh's traffic. The publicly reachable machine hosting your root48+node is its natural home. Each node elects its **own** relay (`relay` in49+[config.json](configuration.md)), and the election travels inside that node's pairing tickets, so50+nothing has to be coordinated across the mesh — no lofi-run infrastructure, no shared choke point.51+Your mesh's availability depends only on machines you operate.52+53+`--relay` accepts several URLs (comma-separated or repeated) for failover. `--no-relay` opts out54+entirely; defensible for nodes that are all mutually reachable (static IPs, a VPN), but it also55+forgoes relay-assisted address discovery, so expect hole-punching to suffer across NATs.56+3257 ## One binary3358 3459 ```sh@@ -8,6 +8,7 @@ lofi-node — self-hostable sync node for lofi apps
88 Usage:99 lofi-node init [--dir <dataDir>] [--app-id <id>] [--port <n>]1010 [--public-url <base>] [--open] [--storage-path <path>] [--memory]11+ [--relay <url[,url…]>] [--no-relay]1112 lofi-node start [--dir <dataDir>]1213 lofi-node pair <node-ticket> [--dir <dataDir>]1314 lofi-node ticket issue [--label <s>] [--url <base>] [--provision] [--dir <dataDir>]@@ -30,6 +31,8 @@ Creates the data directory and writes [`config.json`](configuration.md): a gener
3031 | `--open` | Opt out of ticket gating — anyone who can reach the port can sync. CLI inits default to **ticket-gated**. |3132 | `--storage-path <path>` | SQLite store on any mounted location; probed writable at boot. |3233 | `--memory` | Ephemeral in-memory store. |34+| `--relay <url[,url…]>` | Use your own iroh relay(s) instead of the public n0 servers ([why](beyond-the-lan.md)). Repeatable. |35+| `--no-relay` | No relays at all: direct connections only. |3336 3437 ## start3538 @@ -58,5 +61,6 @@ connections with close code 4001 — no restart, no IPC.
5861 5962 ## status6063 61-Prints configuration and live state without changing anything: access mode, storage, issued tickets,62-upstream election, and mesh state (up with connection stats, off, or unavailable with the reason).64+Prints configuration and live state without changing anything: access mode, storage, relay election,65+issued tickets, upstream election, and mesh state (up with connection stats, off, or unavailable66+with the reason).@@ -19,6 +19,7 @@ variables. Both files are owned by the node; edit `config.json` only with the da
1919 "storage": { "type": "sqlite", "path": "/mnt/nas/lofi" }, // or { "type": "memory" }2020 "publicUrl": "http://192.168.1.10:4802", // base embedded in issued tickets2121 "upstream": "none", // "none" | { "url": "wss://…" } | { "peer": "endpoint…" }22+ "relay": "n0", // "n0" (default when absent) | { "urls": ["https://…"] } | "disabled"2223 "allowLocalFirstAuth": true2324 }2425 ```@@ -30,6 +31,12 @@ variables. Both files are owned by the node; edit `config.json` only with the da
3031 boot) or memory. See [storage choices](storage-choices.md).3132 - **`upstream`** — `"none"` for a root node; `{ "peer": … }` for an iroh-paired leaf3233 ([pairing](pair-two-homes.md)); `{ "url": … }` for a plain HTTP(S) upstream Jazz server.34+- **`relay`** — which relay servers the node's iroh endpoint uses for hole-punching assistance and35+ as a fallback path when a direct connection cannot be established. `"n0"` (the default when36+ absent) uses the public n0-computer relays: rate-limited, no SLA, meant for development and37+ testing. `{ "urls": […] }` points at relays you run ([bring your own relay](beyond-the-lan.md));38+ `"disabled"` forgoes relays entirely, including relay-assisted address discovery. The elected39+ relay travels inside this node's pairing tickets, so peers need no matching configuration.3340 - **Secrets** stay in this file and never transit issued tickets; a provision-scoped ticket causes3441 the gate to inject `adminSecret` server-side rather than revealing it.3542 @@ -47,6 +47,15 @@ two states are honest degradations: `{ state: "off" }` when the mesh is disabled
4747 `{ state: "unavailable", reason }` when the native layer could not load — the Jazz server still runs4848 LAN-only, and `ticket()`/`pair()` throw `MeshUnavailableError` rather than pretending.4949 50+## The relay's role51+52+Relays assist that hole-punching (address discovery) and carry traffic only for the connections that53+cannot go direct; in practice the large majority of iroh connections are direct. The relay is a54+**per-node election** (`relay` in [config.json](configuration.md)): whichever relay a node elects55+travels inside its pairing ticket, so the dialing side needs no matching configuration. The default56+is n0-computer's public relays, which are rate-limited and meant for development. For a production57+mesh, [bring your own](beyond-the-lan.md).58+5059 ## Choosing a root5160 5261 The root holds the authoritative store; leaves relay to it. Put the root on the machine with the