clone

a self-hosted sync node for lofi apps: jazz sync over iroh, ticket-gated, with no static ip or cloud needed

feat/write-expiry-groundwork
lofi-node / docs / site / cli.md
cli.md 3.7K source

CLI

lofi-node — self-hostable sync node for lofi apps

Usage:
  lofi-node init   [--dir <dataDir>] [--app-id <id>] [--port <n>]
                   [--public-url <base>] [--open] [--storage-path <path>] [--memory]
                   [--relay <url[,url…]>] [--no-relay]
  lofi-node start  [--dir <dataDir>]
  lofi-node pair   <node-ticket> [--dir <dataDir>]
  lofi-node ticket issue  [--label <s>] [--url <base>] [--provision] [--dir <dataDir>]
  lofi-node ticket list   [--dir <dataDir>]
  lofi-node ticket revoke <id> [--dir <dataDir>]
  lofi-node status [--dir <dataDir>]

The data directory defaults to ./lofi-node-data everywhere.

init

Creates the data directory and writes config.json: a generated Jazz app id (or --app-id to pin one), fresh backend and admin secrets, and your choices.

Flag Meaning
--port <n> Fixed public port for the gate; auto-allocated when omitted.
--public-url <base> Base URL embedded in issued tickets (use the reachable address).
--open Opt out of ticket gating — anyone who can reach the port can sync. CLI inits default to ticket-gated.
--storage-path <path> SQLite store on any mounted location; probed writable at boot.
--memory Ephemeral in-memory store.
--relay <url[,url…]> Use your own iroh relay(s) instead of the public n0 servers (why). Repeatable.
--no-relay No relays at all: direct connections only.

start

Starts the daemon from an initialized directory: boots the loopback-only Jazz server, opens the public gate, loads the native transport when available, and prints the gate URL plus the node-pairing ticket when the mesh is up.

pair

lofi-node pair <node-ticket> elects the peer as this node's upstream — the node becomes a leaf relaying sync and catalogue traffic over iroh. The argument is a node-pairing endpoint… string from the peer's start output, not an app ticket (the CLI rejects app tickets with a pointed message). Re-electable at runtime; the public port does not change.

ticket issue / list / revoke

issue mints an app-connect ticket and prints it exactly once — the node stores only a digest. --label names it for your bookkeeping, --url overrides the embedded base for this ticket, and --provision mints a provision-scoped ticket that can also administer the store (scopes).

list shows every issued ticket: id, scope, label, issued time, and revocation state — including tickets revoked through their lineage, since a derived sync ticket dies with its parent provision ticket. revoke <id> invalidates one (and everything derived from it); a running daemon picks the change up within seconds and closes the affected live connections with close code 4001 — no restart, no IPC.

status

Prints configuration and live state without changing anything: access mode, storage, relay election, issued tickets, upstream election, and mesh state (up with connection stats, off, or unavailable with the reason).