lnurlcash-kit 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/llms.txt CHANGED
@@ -24,9 +24,32 @@ splitNote(callback, k1s, amountMsat, opts?) -> {k1, change, signature?, changeSi
24
24
  mergeNotes(callback, k1s, opts?) -> {k1, signature?}
25
25
  meltNote(callback, k1, bolt11, opts?) -> {pr?, verify?}
26
26
  settleNote(baseUrl, k1, expectedMsat, sig?, opts?) -> {k1, amountMsat, signature?, callback}
27
+ settleNoteForValue(noteUrl, {mints, minMsat, requireSignature?}, opts?)
28
+ -> {note, newUrl} server side: accept a note as payment, rotate to settle
27
29
  probeBurnedNote(url, opts?) -> 'live' | 'gone' | 'unknown'
28
- verifyNoteSignature(k1, amountMsat, sigHex, mintPubkeyHex) -> boolean
29
- fetchPayRequest(url, opts?) / requestInvoice(cb, msat, opts?) / fetchInvoiceVerification(url, opts?)
30
+ verifyNoteSignature(k1, amountMsat, sigHex, pubkeyHex | pubkeyHex[]) -> boolean
31
+ verifyNoteSignatureAgainst(k1, amountMsat, sigHex, keys) -> {valid, pubkey}
32
+ fetchMintAddress(url, opts?) -> {mintPubkey?, payLink, name?, motd?, contact?,
33
+ tosUrl?, fees?, version?, previousPubkeys?, mintToHash?, node*?}
34
+ experimental, often absent
35
+ mintPubkey verifies note signatures. It is NOT the node key in nodeUri.
36
+ nodePubkey is a deprecated alias for mintPubkey, dropped next breaking change.
37
+ mintToHash is the fallback copy of the payRequest's flag - see 13.
38
+ deriveNoteRoot(seedBytes) -> Uint8Array HMAC-SHA256("lnurlcash-note-v1", seed)
39
+ deriveNoteSecret(root, host, index) -> k1 HMAC-SHA256(root, host + ":" + index)
40
+ derivedSecretSource(root, host, start) -> RandomSecret & {index()}
41
+ restoreNotes(baseUrl, root, host, {gap?, start?}, opts?) -> {found[], next}
42
+ fetchPayRequest(url, opts?) -> {callback, minSendable, maxSendable, metadata,
43
+ withdrawLink?, mintFee?, mintToHash?} mintToHash: mint accepts an `h`
44
+ fetchInvoiceVerification(url, opts?)
45
+ requestInvoice(cb, msat, {h?, ...opts}?) -> {pr, verify?, disposable, mintToHash}
46
+ claimMintedNote(withdrawLink, k1, opts?)
47
+ -> {state: 'minted'|'unminted'|'pending'|'spent', k1, amountMsat, callback}
48
+ encodePaymentRequest(req) -> "lnurlcashreq1..." base64url(JCS JSON), prefix
49
+ decodePaymentRequest(str, {now?}) -> PaymentRequest throws ProtocolError
50
+ isPaymentRequest(str) -> boolean true for an expired one too
51
+ paymentRequestAmountMsat(req) -> msat request amount is in SAT, this is the
52
+ only sat-denominated field in the library; never multiply by hand
30
53
  parseMintFee(metadata) / applyMintFee(gross, fee) / grossUpForMintFee(net, fee)
31
54
  createClient(opts) -> all of the above with opts bound
32
55
 
@@ -38,26 +61,64 @@ Options (always last): {fetch?, timeoutMs?, offline?, randomSecret?}
38
61
  `amount` in a note URL is an unverified claim.
39
62
  2. On rotate/split/merge the WALLET generates the replacement secret and
40
63
  discloses only sha256(secret). Never accept a service-supplied k1.
41
- 3. Catch AmbiguousMutationError, PERSIST err.newSecrets before anything
42
- else, then call probeBurnedNote to learn what happened. Treating it as
43
- failure destroys money the service may already have minted.
64
+ 3. On ANY error from a mutation call newSecretsOf(err) FIRST, persist what
65
+ it returns, then call probeBurnedNote to learn what happened. Treating a
66
+ failure as a failure destroys money the service may already have minted.
67
+ AmbiguousMutationError always carries secrets; so does a NoteSpentError
68
+ or NoteUnknownError from a mutation, because that is also what a retried
69
+ GET looks like once its first attempt burned the input.
44
70
  4. RequestRefusedError means nothing was sent - safe to treat as no-op.
45
71
  5. A melt's OK means IN FLIGHT, not spent. PendingNoteError means retry, not
46
72
  spent.
47
73
  6. Rotate immediately after claiming a minted note: the mint generated that
48
74
  preimage, and LUD-21 verify exposes it to anyone who saw the invoice.
75
+ Does not apply to a note minted with `h` - see 13.
49
76
  7. Never log note URLs. The secret is in the query string.
50
77
  8. Never branch on error message text. Branch on the error class.
78
+ 9. lnurlcashreq1 means ONE schema: {v: 1, id (16 hex), amount (whole sat as
79
+ a decimal string), currency: "sat", methodDetails: {mints}, to?, memo?,
80
+ expires?}, JCS-canonicalised, base64url, no padding. Validation is
81
+ strict, an unrecognised field included. At the expiry is expired. Pass
82
+ {now: 0} to show a user what they scanned. The decoder also reads the
83
+ short {a, m, u} form an early 402 rail emitted under this prefix, but
84
+ NEVER emit it.
85
+ 10. Accepting a note as payment MUST end in a rotate. Checking its value
86
+ and granting access without rotating leaves the payer able to spend it
87
+ again; the rotate is both the ownership transfer and the replay check.
88
+ 11. A mint may rotate its signing key. Verify against its current key plus
89
+ the previousPubkeys it publishes; a note that verifies only against a
90
+ retired key should be rotated to be re-signed. An empty key list is a
91
+ rejection.
92
+ 12. With derived secrets, persist the per-host counter in the SAME write
93
+ that stages the new records and BEFORE the hash goes on the wire. A
94
+ split consumes two indices, a rotate one; read source.index() for the
95
+ next unused one. Bumping late re-derives a secret the mint has already
96
+ seen.
97
+ 13. Name the note you are buying where the mint offers it. Read mintToHash
98
+ off fetchPayRequest FIRST (fetchMintAddress is the fallback for a mint
99
+ that only says it on that optional document), then derive a secret,
100
+ persist it, and pass h = hashK1(secret) to requestInvoice. The mint
101
+ credits the note at that hash, the payment preimage is then not a valid
102
+ k1, and you claim with claimMintedNote rather than polling verify - no
103
+ rotate needed, and the note is seed-derived from birth so restoreNotes
104
+ finds it. Persist the secret BEFORE requesting the invoice. Three
105
+ places carry the flag: the payRequest and the mint address both mean
106
+ "I accept an h", InvoiceResult.mintToHash means "I bound THIS quote to
107
+ your hash". Anything not exactly true is no. A mint that advertises
108
+ nowhere ignores the h and the preimage path is unchanged.
51
109
 
52
110
  ## Error classes
53
111
 
54
112
  RequestRefusedError nothing sent, note untouched
55
113
  ServiceRejectedError processed and refused (definitive)
56
114
  PendingNoteError a melt is in flight on this k1 - retry
57
- NoteSpentError authoritative: already burned
115
+ NoteSpentError authoritative: already burned - but from a MUTATION it
116
+ may be a retry whose first attempt landed; read
117
+ newSecretsOf(err) before believing it
58
118
  NoteUnknownError service does not recognise it
59
119
  AmbiguousMintError outcome UNKNOWN - assume nothing
60
120
  AmbiguousMutationError carries .newSecrets - persist them
121
+ newSecretsOf(err) -> string[] the secrets any error is carrying, or none
61
122
  ProtocolError a non-mutating response did not match the spec
62
123
 
63
124
  ## Conformance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lnurlcash-kit",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "LNURLcash (LUD-25) bearer note client for TypeScript - mint, rotate, split, merge, melt, and verify offline",
5
5
  "author": "TheCryptoDonkey",
6
6
  "license": "MIT",