@forgesworn/moneyer 0.9.0 → 0.9.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.9.1 - 2026-09-01
6
+
7
+ **Fix: the LUD-25 informational GET now accepts `h=sha256(k1)`.** A
8
+ receipt-aware wallet can verify that a bound mint payment created its note
9
+ without disclosing the bearer secret. The hash-only reply deliberately omits
10
+ `k1`; lazy settlement works on this path as it does on the secret-bearing
11
+ path, and conflicting `k1` and `h` parameters are refused.
12
+
5
13
  ## 0.9.0 - 2026-08-31
6
14
 
7
15
  **LUD-25 comment-bound minting is now unconditional.** Every mint quote must
package/README.md CHANGED
@@ -576,7 +576,7 @@ clearnet.
576
576
  | `/p/cb` | LUD-06 pay callback; issues the mint invoice, and takes an optional `h` naming the note |
577
577
  | `/z/cb/<zap name>` | the zap callback; validates the kind 9734 and issues the invoice |
578
578
  | `/verify/<hash>` | LUD-21 verify, for mint invoices and melt payments |
579
- | `/w` | LUD-03 informational GET; a live note also carries `payLink`, the route back to this mint's discovery document |
579
+ | `/w` | LUD-03 informational GET; accepts `k1` or the non-disclosing LUD-25 `h=sha256(k1)` check; a live note also carries `payLink` |
580
580
  | `/w/cb` | the mutating callback: melt, rotate, split, merge |
581
581
  | `POST /names` | claim a lightning address, authenticated by NIP-98 |
582
582
  | `/.well-known/nostr.json` | NIP-05 for the names this mint serves |
package/dist/server.js CHANGED
@@ -140,8 +140,7 @@ export const createMoneyer = async (config, deps = {}) => {
140
140
  // A note whose id we do not know yet may be a settled mint invoice whose
141
141
  // claim simply has not been observed: settle it lazily against the
142
142
  // funding source, which is what makes paying an invoice mint the note.
143
- const resolveNote = async (k1) => {
144
- const id = hashK1(k1);
143
+ const resolveNoteId = async (id) => {
145
144
  const note = store.noteById(id);
146
145
  if (note)
147
146
  return note;
@@ -161,6 +160,7 @@ export const createMoneyer = async (config, deps = {}) => {
161
160
  }
162
161
  return null;
163
162
  };
163
+ const resolveNote = async (k1) => resolveNoteId(hashK1(k1));
164
164
  // ---- transparency: what the mint owes, and what the node holds ----
165
165
  //
166
166
  // Cached for 30 seconds so a public endpoint cannot be turned into a
@@ -754,10 +754,22 @@ export const createMoneyer = async (config, deps = {}) => {
754
754
  }
755
755
  // ---- LUD-03 informational GET ----
756
756
  if (requestUrl.pathname === '/w') {
757
+ const hasK1 = q.has('k1');
758
+ const hasH = q.has('h');
757
759
  const k1 = q.get('k1')?.toLowerCase();
758
- if (!k1 || !HEX32.test(k1))
760
+ const h = q.get('h')?.toLowerCase();
761
+ if ((!hasK1 && !hasH) ||
762
+ (hasK1 && (!k1 || !HEX32.test(k1))) ||
763
+ (hasH && (!h || !HEX32.test(h)))) {
764
+ return fail('Unknown note.');
765
+ }
766
+ // LUD-25's hash-only check lets a wallet prove that the note it just
767
+ // bought exists without sending the bearer secret to the service a
768
+ // second time. If both spellings are present, they must name the same
769
+ // note; choosing one would make a malformed URL an ownership oracle.
770
+ if (k1 && h && hashK1(k1) !== h)
759
771
  return fail('Unknown note.');
760
- const note = await resolveNote(k1);
772
+ const note = h ? await resolveNoteId(h) : await resolveNote(k1);
761
773
  if (!note)
762
774
  return fail('Unknown note.');
763
775
  if (note.state === 'burned')
@@ -780,7 +792,10 @@ export const createMoneyer = async (config, deps = {}) => {
780
792
  return send({
781
793
  tag: 'withdrawRequest',
782
794
  callback: `${origin}/w/cb`,
783
- k1,
795
+ // A hash-only query deliberately does not echo or invent k1. The
796
+ // wallet already has the secret; this endpoint only confirms the
797
+ // mint's record at sha256(k1).
798
+ ...(k1 ? { k1 } : {}),
784
799
  minWithdrawable: wholeSatFloor(note.amountMsat),
785
800
  maxWithdrawable: note.amountMsat,
786
801
  defaultDescription: config.description,
package/llms.txt CHANGED
@@ -82,5 +82,5 @@ NoteStore - SQLite store; notes by sha256(k1), never secrets
82
82
  /.well-known/lnurlw/{user} LUD-25 mint address (experimental)
83
83
  /p/cb LUD-06 pay callback (optional h names the note)
84
84
  /verify/{payment_hash} LUD-21 verify (mint invoices AND melt payments)
85
- /w LUD-03 informational GET
85
+ /w LUD-03 informational GET by k1 or hash-only h
86
86
  /w/cb melt / rotate / split / merge
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgesworn/moneyer",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "An LNURLcash (LUD-25) mint - strikes Lightning bearer notes. Independent implementation, cln/lnd funding sources, SQLite, zero HTTP framework.",
5
5
  "author": "TheCryptoDonkey",
6
6
  "license": "MIT",