The GitHub social banner (docs/assets/brand, from the site's /node visual language) heads the README, which now mirrors lofi's structure: positioning + links (lofi.host/node docs/api, node-scoped llms.txt pair, JSR, license) + quick start + contract pointers. Duplicated guide content (what-you-get, architecture, platform tables) moved to the site; the normative contracts (app-ticket.md, hosting walkthrough, UPSTREAM) stay repo-side. Banner added to the JSR publish include so the registry README renders it.
- commit
- 5ce7cd8eee68232427e71545ac9578bf98b7f97d
- parent
- 1f83f723ce3cac025800407806aa5c4f51bc8a23
- author
- Dami <dami.etoile@gmail.com>
| M | README.md | 99 | +++++++----------------------- |
| M | deno.json | 1 | + |
@@ -1,22 +1,26 @@
1+2+13 # @nzip/lofi-node24 3-The first-class way to self-host the sync backend for4-[lofi](https://github.com/FelineStateMachine/lofi) apps: one daemon embedding a Jazz 2.0 sync server5-plus [iroh](https://iroh.computer) node-to-node transport. Browsers keep speaking Jazz's protocol —6-`JAZZ_SERVER_URL` points at your node and lofi's `adapter: "jazz"` contract is untouched. Two nodes7-pair by ticket and replicate over iroh: dialed by key, hole-punched, no static IPs, no cloud8-dependency.5+**Self-host the sync backend for [lofi](https://github.com/FelineStateMachine/lofi) apps: one daemon6+embedding a Jazz sync server, iroh peer-to-peer transport, and ticket-gated access.**7+8+Issue a ticket carrying location + secret; the app stores it passkey-encrypted and syncs against9+your node. Pair homes by ticket — no static IPs, no cloud — and choose where your data lives.10+11+[Documentation](https://lofi.host/node) · [API reference](https://lofi.host/node/api) ·12+[JSR package](https://jsr.io/@nzip/lofi-node) · [MIT License](LICENSE)913 10-- Hosting walkthrough: [docs/hosting-lofi-apps.md](docs/hosting-lofi-apps.md)11-- Native layer provenance: [native/iroh-js/UPSTREAM.md](native/iroh-js/UPSTREAM.md)12-- License: [MIT](LICENSE)14+Building with an AI agent? Feed it [llms.txt](https://lofi.host/node/llms.txt) for an index of the15+node documentation, or [llms-full.txt](https://lofi.host/node/llms-full.txt) for the complete docs16+and API reference in one file.1317 1418 ## Quick start1519 1620 Run straight from JSR with `dx` (Deno's npx — `deno x`):1721 1822 ```sh19-dx -A jsr:@nzip/lofi-node/cli init --port 4802 # ticket-gated by default; --open opts out23+dx -A jsr:@nzip/lofi-node/cli init --port 4802 # ticket-gated by default2024 dx -A jsr:@nzip/lofi-node/cli start # gate URL + node-pairing ticket2125 dx -A jsr:@nzip/lofi-node/cli ticket issue --label phone # app-connect ticket (shown once)2226 ```@@ -45,74 +49,18 @@ As a library:
4549 deno add jsr:@nzip/lofi-node4650 ```4751 48-The issued `lofisync1.…` ticket carries **location + secret**; the lofi app stores it49-passkey-encrypted and uses its URL as the sync server — possession of the ticket is access,50-revocation is `ticket revoke` (live sockets close with 4001). Format contract:51-[docs/app-ticket.md](docs/app-ticket.md). Storage location is the user's choice (`--storage-path`52-for NAS/mounted volumes, `--memory` for ephemeral).53-54-Or embed it:55-5652 ```ts5753 import { createSyncNode } from "@nzip/lofi-node";5854 59-const node = await createSyncNode({60- appId,61- backendSecret,62- adminSecret,63- dataDir: "./data",64- upstream: { peer: "endpoint…" }, // or { url: "wss://…" } or "none"65-});55+const node = await createSyncNode({ appId, backendSecret, adminSecret, dataDir: "./data" });6656 node.url; // -> JAZZ_SERVER_URL for the lofi app67-node.ticket(); // -> share with the peer node68-await node.pair(otherTicket); // re-elect upstream at runtime; port unchanged69-node.status(); // jazz health, upstream, mesh + live conn stats (direct/relay, rtt)7057 ```7158 72-## What you get73-74-- **A real Jazz sync server** (jazz-napi `JazzServer`, SQLite-backed, `/health` endpoint,75- local-first auth) that any lofi app can use by URL.76-- **Node-to-node replication over iroh**: pairing tickets are upstream `EndpointTicket` strings;77- sync and catalogue traffic (WS + HTTP) tunnel over QUIC, so two homes converge without port78- forwarding. Administering either node reaches the root through the tunnel.79-- **Observability**: `status().mesh.connections` reports live tunnel connections with rtt and path80- counts (direct vs relay).81-- **No silent degradation**: if the native layer can't load, the Jazz server still runs (LAN-only)82- and `status()` says exactly why pairing is off.83-- **One binary**: `deno task compile` embeds the prebuilt native matrix and extracts it to a84- version-keyed OS cache at first run.85-86-## Where lofi-node fits87-88-- The data plane is Jazz 2.0 **alpha**, pinned exactly (`jazz-tools@2.0.0-alpha.53`); a node must89- run the same alpha as the app it serves — treat version bumps as coordinated. Early-stage90- software.91-- Platforms: macOS arm64, Linux x86_64/aarch64 (prebuilt in-repo; or `deno task native` with a Rust92- toolchain). **Windows is a documented gap**: napi-build's `*-gnu` linking needs a libnode.dll93- import library (upstream ships msvc, delay-loaded); a Windows artifact needs cargo-xwin or a94- Windows CI runner. Until then Windows runs LAN-only with a typed `mesh: unavailable` reason.95-- Schema deploys to a self-hosted node use the jazz-tools schema-project flow — see the hosting96- walkthrough, including the current alpha caveat on lofi's browser convergence gate (fails97- identically against lofi's own managed dev server; lofi-node has behavior parity).98-99-## Architecture100-101-| Piece | Where |102-| --------------------------------------- | --------------------------------------------- |103-| One-constructor library | `src/node.ts` (`createSyncNode`) |104-| WS+HTTP-over-iroh tunnel | `src/tunnel.ts` (1 iroh conn : 1 ws / 1 http) |105-| Vendored iroh-js napi crate (iroh 1.x) | `native/iroh-js/` (+ `lofi_ext.rs` framing) |106-| Addon loader (dev shim / cache extract) | `src/native/addon.ts`, `src/native/loader.ts` |107-| Adapter over the addon | `src/iroh/node.ts` (`IrohNode`/`IrohConn`) |108-| Jazz wrapper (pinned alpha) | `src/jazz.ts` |109-| CLI: init / start / pair / status | `cli.ts` |110-| In-process test mesh (no iroh) | `testing/mod.ts` |111-112-The native layer is a trimmed, provenance-tracked vendor of upstream113-[n0-computer/iroh-ffi](https://github.com/n0-computer/iroh-ffi)'s `iroh-js` napi crate —114-module-level vendoring, byte-identical files, every local edit marked `// lofi-node:`, one extension115-module, tag bumps by `diff -r`. Design history: [docs/port-iroh-js.md](docs/port-iroh-js.md).59+Tutorials (self-hosting, pairing, store provisioning), guides, and reference live at60+[lofi.host/node](https://lofi.host/node). The normative contracts stay in this repo:61+[docs/app-ticket.md](docs/app-ticket.md) (ticket format, scopes, store-status — with62+[conformance fixtures](docs/fixtures/app-ticket-fixtures.json)) and63+[docs/hosting-lofi-apps.md](docs/hosting-lofi-apps.md) (validated hosting walkthrough).11664 11765 ## Developing lofi-node11866 @@ -122,6 +70,7 @@ deno task check # fmt --check + lint + typecheck — the PR gate
12270 deno task test # full suite; iroh/jazz integration auto-skips without the addon12371 ```12472 125-Cross-compiling the prebuilt matrix runs on any x86_64 Linux host with Nix:126-`nix develop -c ./scripts/cross-build.sh` in `native/iroh-js/`. See127-[CONTRIBUTING.md](CONTRIBUTING.md) for boundaries (vendoring rules, secrets policy, version pins).73+The native layer is a provenance-tracked vendor of upstream74+[n0-computer/iroh-ffi](https://github.com/n0-computer/iroh-ffi)'s `iroh-js` crate — see75+[native/iroh-js/UPSTREAM.md](native/iroh-js/UPSTREAM.md) for the vendoring contract and76+[CONTRIBUTING.md](CONTRIBUTING.md) for boundaries and the release procedure.