FARRIER-KIT

Lightning payment primitives · v1.1.3

Decode it. Prove it.
Then believe it.

farrier-kit is the read-and-verify layer for Lightning: what an invoice commits to, whether a preimage proves it was paid, and whether a URL is safe to fetch. Browser and Node run the same code, and the bench below is that code, in your browser, right now.

Use the bench npm install farrier-kit

One runtime dependency (@noble/hashes)  ·  ESM and CJS  ·  independently security-reviewed  ·  MIT

The sample is a BOLT-11 specification vector: unsigned, long expired and worth nothing. Change one character of it and watch the checksum refuse.

01

The vectors, run here, now

farrier-kit ships language-neutral conformance vectors so a port can prove it agrees byte for byte. They are not a private test fixture: they are the contract, and this button runs the whole file set against the bundle you would install.

not run yet

Tier 1 is correctness, tier 2 is policy. A conformant decoder must match tier 1. Tier 2 is farrier-kit refusing things the spec merely leaves open — an ambiguous amount in the human-readable part, a numeric field that would overflow — and a port that wants security parity should match those too. The next milestone is a Kotlin build of the pure surface checked against these same files, so a native Android app verifies a payment identically to the web.

02

The narrow slice

Most Lightning libraries either just decode invoices or are whole wallet toolkits. farrier-kit takes the security-critical gap they leave: safely resolving where to pay, and proving that a payment happened.

/bolt11
Amount, payment hash, network, expiry, description and description hash. No signature recovery and no route hints, because a payer's wallet does that. It answers one question: what does this invoice commit to?
/preimage
payment_hash = SHA-256(preimage), compared in constant time. This is the only thing that proves a payment settled, and it is the check most integrations skip.
/lnurl
LUD-06 and LUD-16 resolution, LUD-21 verification, an SSRF guard and a four-way gate on the invoice that comes back — amount, description hash, network and payment hash all checked before it reaches you.
/http
A timeout- and size-capped, redirect-safe JSON fetch. Every one of those limits exists because an untrusted endpoint is on the other end.
/node
The one Node-only entry: DNS-pinned fetch, so a hostname that resolves inward is caught where the browser cannot look. Gated out of the browser bundle and never reachable from the root.

Not in scope, deliberately

  • Sending a payment, or any wallet function
  • WebLN, boostagrams, L402, BIP-21
  • NWC and wallet transport — that is nwc-kit
  • General Nostr cryptography
  • Fiat pricing and unrelated handle formats

An invoice's payment_hash alone is not a commitment. The payee picks the preimage, so they can mint a second invoice carrying the same hash and a different amount. Pass the agreed amount to verifyInvoiceCommitment whenever real money is about to move; the bench's third tab exists to make that failure visible.

§

The rest of the rail

Each of these does one job and refuses the next one. Together they are a payment rail an application can assemble itself, without any single piece asking to be trusted with everything.