Releases: FelineStateMachine/lofi-node
Release list
v0.5.0
lofi-node 0.5.0 adds device-bound sync tickets and connection visibility.
Device-bound sync tickets (proof of possession)
A sync ticket can now be bound to the device that enrolls it. When a lofi app offers a device public key at the scope-down exchange, the derived sync ticket only works together with a signature from that device's key — which never leaves the device. A ticket string that leaks through a backup, a log, or a copied URL no longer grants access on its own.
- The bare secret of a bound ticket answers only liveness checks and the possession exchange; all sync traffic requires a short-lived connect token minted by a fresh signature.
- Challenges are single-use and expire quickly; every failure answers with the same response, so probing reveals nothing.
- Connect tokens live in memory only: a node restart invalidates them and clients re-run the exchange on their next start. Revoking a ticket kills its tokens and closes its live connections, exactly as before.
- Unbound tickets are unchanged. Apps and nodes on different versions fall back to the existing bearer behavior in every combination.
Binding protects against theft of the ticket string. It does not protect against code running inside the app's own page while it is open, and a node build predating this release serves a bound ticket as a plain bearer ticket — both stated in the ticket contract (docs/app-ticket.md).
Connection visibility
lofi-node ticket listnow shows when each ticket was last seen, answering "is that phone still using this ticket?" before revoking it. Last-seen survives node restarts and is written by the running node about once a minute.node.status()reports live gated connections per ticket, joined with ticket labels, for embedders and the startup summary.
Assets
Compiled binaries for macOS (Apple silicon) and Linux x86_64, plus the native addon libraries the JSR package downloads and verifies by digest. The native addon is unchanged from 0.4.0.
v0.4.0 — The write-verdict taxonomy
The adjudication release: rejected writes now carry a classification the client can act on. The node keys every mutation error to permanent or transient, names the four codes that settle a write for good, and states the health/connection and expiry contracts as the reference the client side builds against.
Features
- Mutation-error taxonomy (#11): every write rejection is classified permanent — the store's final verdict, safe for the client to roll back and compensate — or transient, keyed by the error code. Unknown codes default to transient, so a new server code never falsely retires a pending write. A deterministic write-rejection harness proves verdict correlation to the originating write and re-derivation across reconnects, and
GET /healthis exercised in open, ticket, and peer modes. - The four permanent codes (#13):
permission_denied,transaction_conflict,permissions_head_missing, andexpiredare registered permanent.expiredis groundwork: the classification ships now so clients written today handle it correctly when server-enforced write expiry lands.
Contracts and documentation
- The write-verdicts page states the four-code taxonomy and the health/connection contract as the node actually answers them (#11, #13)
- The expiry contract is specified mechanism-neutral — what an expired write means, who may retire it, and why the client never does — and the design record now serves as the reference spec for the client-side work tracked in lofi#145, with the upstream ask drafted (#13)
Full Changelog: v0.3.0...v0.4.0
v0.3.0 — Scope-down tickets, owner-only secrets, adopted site docs
The custody release: tickets gain a second capability tier without a second paste, secret files become owner-only on disk, and the docs-site pages for the /node section now live in this repository so they version with the node.
Features
- Scope-down ticket exchange (#4):
POST /t/<secret>/derive-sync-ticketlets a provision-scoped ticket mint a complete, parent-linked sync ticket. Revoking the parent revokes everything derived from it — live derived sockets close with4001— andticket listshows lineage revocation. Sync-scoped and unknown secrets get the standard non-enumerable401;tickets.jsonstays v1 (additiveparentId). This is what lets a lofi app persist transport-only credential material and custody the provision original behind the user's passkey.
Security
- Owner-only secret files (#3):
config.json(holding the backend and admin secrets) is written0600inside a0700data directory, and installs created before modes were set are healed on every load —config.json,iroh.key, and the directory alike. POSIX only; mode assertions in tests, skipped on Windows.
Documentation
- The 13 site-voice pages for lofi.host's /node section are adopted under
docs/site/, gated by this repo's CI and consumed by the lofi site at a pinned checkout (#6) - Ticket custody is stated as the app actually ships it — sealed at rest, provision capability passkey-gated, password manager as the durable copy (#5, #8)
- Browser convergence is recorded as validated against lofi-node, with the benign boot-time warning named (#7)
- Operator hardening: proxy-log redaction with concrete Caddy and nginx configs, the secret-in-URL rationale, and app-shell weight/precompression guidance (#8)
Full Changelog: v0.2.1...v0.3.0
v0.2.1
v0.2.0
v0.1.3
v0.1.2
v0.1.1
v0.1.0
Full Changelog: https://github.com/FelineStateMachine/lofi-node/commits/v0.1.0