@forgesworn/moneyer 0.7.0 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.0 - 2026-08-30
4
+
5
+ **`MONEYER_REQUIRE_COMMENT` (default off).** Refuses a mint quote that names
6
+ no output, rather than falling back to a note keyed by the payment preimage.
7
+ Off by default because LUD-25 line 80 still asks for that fallback; on, the
8
+ refusal happens before any invoice is issued, so a wallet never pays for a
9
+ quote the mint was always going to reject. A quote naming its output by
10
+ either spelling (`comment` or `h`) is unaffected.
11
+
12
+ Worth knowing: `lnurlcash-conformance` now grades the mandate as required, so
13
+ moneyer fails that suite in its default configuration and passes with the
14
+ flag on. See that repo's `docs/COMMENT-IS-MANDATORY.md`.
15
+
3
16
  ## [0.7.0] - 2026-08-26
4
17
 
5
18
  - **The published node capacity is the announced one.** `nodeCapacity` in
package/README.md CHANGED
@@ -105,6 +105,7 @@ default, and a variable set to an empty string counts as unset.
105
105
  | `MONEYER_MIN_SENDABLE_MSAT` | `1000` | smallest payment the mint advertises |
106
106
  | `MONEYER_MAX_SENDABLE_MSAT` | `100000000` | largest payment the mint advertises |
107
107
  | `MONEYER_MIN_MINT_MSAT` | `1000` | dust floor: the smallest note the mint will strike |
108
+ | `MONEYER_REQUIRE_COMMENT` | `false` | refuse a mint quote that names no output, instead of falling back to a note keyed by the payment preimage. See [comment protection](#comment-protection) |
108
109
  | `MONEYER_MAX_K1S` | `21` | most notes one callback may name |
109
110
  | `MONEYER_VERIFY` | `true` | the LUD-21 `verify` endpoint. Off means 404 |
110
111
  | `MONEYER_WALLET_URL` | | a companion web wallet the mint's site links notes into |
@@ -667,3 +668,34 @@ in [awesome-lnurlcash](https://github.com/TheCryptoDonkey/awesome-lnurlcash).
667
668
  ## Licence
668
669
 
669
670
  MIT.
671
+
672
+
673
+ ## Comment protection
674
+
675
+ A LUD-25 mint quote may carry a LUD-12 `comment` holding
676
+ `hex(sha256(secret))`, naming the note the payment will mint. The note is
677
+ then keyed by the wallet's own `secret`, and the payment preimage redeems
678
+ nothing.
679
+
680
+ With no such comment, LUD-25 line 80 says the mint MUST fall back to keying
681
+ the note by the payment preimage itself. That fallback is the default here,
682
+ and it is what the draft currently requires.
683
+
684
+ `MONEYER_REQUIRE_COMMENT=true` refuses an unnamed quote instead, before any
685
+ invoice is issued. Two reasons to want it:
686
+
687
+ - A preimage-keyed note is only as safe as the discretion of every routing
688
+ hop on the payment. Each one learns the preimage as it settles its own
689
+ HTLC, often before the payer has finished processing the payment.
690
+ - A funding source that settles without producing a preimage - a Spark
691
+ backend, say - has nothing to key a fallback note by at all.
692
+
693
+ The cost is backward compatibility: a wallet that has never heard of
694
+ LNURLcash sends a bare LUD-06 request and gets an error rather than an
695
+ invoice. `dni/lnurl-mint` made this behaviour unconditional in `b257d58`;
696
+ the draft has not yet followed, which is why it is opt-in here. See
697
+ `lnurlcash-conformance/docs/COMMENT-IS-MANDATORY.md`.
698
+
699
+ Note that `lnurlcash-conformance` now grades the mandate as required, so
700
+ this mint **fails that suite in its default configuration** and passes with
701
+ `MONEYER_REQUIRE_COMMENT=true`.
@@ -6,8 +6,11 @@ import { PaymentAlreadyKnownError, PaymentFailedError, PaymentPendingError } fro
6
6
  // one (r_preimage), which is what lets it back a mint. Payment send/track
7
7
  // are chunked NDJSON streams read line by line to a terminal status.
8
8
  //
9
- // Not yet exercised against a live node - the semantics are a direct port
10
- // of the reference mint's lnd backend, which is.
9
+ // This is what backs the public mint, so the invoice and payment paths run
10
+ // against a live node continuously; the semantics started as a direct port
11
+ // of the reference mint's lnd backend. The exception is nodeInfo's graph
12
+ // self-lookup, which is newer than the running deployment - it is covered
13
+ // by test/lnd-node-info.test.ts and by nothing else yet.
11
14
  //
12
15
  // TLS: for lnd's self-signed cert, point NODE_EXTRA_CA_CERTS at it.
13
16
  const FAILURE_REASONS = {
package/dist/config.d.ts CHANGED
@@ -32,6 +32,7 @@ export type MoneyerConfig = {
32
32
  maxSendableMsat: number;
33
33
  minMintMsat: number;
34
34
  mintFee: MintFee | null;
35
+ requireComment: boolean;
35
36
  roundFeeToSat?: boolean;
36
37
  signingKey?: string;
37
38
  previousSigningPubkeys?: string[];
package/dist/config.js CHANGED
@@ -84,6 +84,7 @@ const contactFromEnv = (env) => {
84
84
  // that starts with a half-understood configuration is holding other
85
85
  // people's money on a misunderstanding.
86
86
  export const configFromEnv = (env = process.env) => {
87
+ const requireComment = flag(env.MONEYER_REQUIRE_COMMENT, false);
87
88
  const baseFeeMsat = int(env.MONEYER_BASE_FEE_MSAT, 0);
88
89
  const feePpm = int(env.MONEYER_FEE_PPM, 0);
89
90
  if (feePpm >= 1_000_000) {
@@ -211,6 +212,7 @@ export const configFromEnv = (env = process.env) => {
211
212
  maxSendableMsat,
212
213
  minMintMsat: int(env.MONEYER_MIN_MINT_MSAT, DEFAULTS.minMintMsat),
213
214
  mintFee: baseFeeMsat === 0 && feePpm === 0 ? null : { baseFeeMsat, feePpm },
215
+ requireComment,
214
216
  roundFeeToSat: flag(env.MONEYER_ROUND_FEE_TO_SAT, DEFAULTS.roundFeeToSat),
215
217
  ...(signingKey ? { signingKey: signingKey.toLowerCase() } : {}),
216
218
  ...(previousSigningPubkeys.length ? { previousSigningPubkeys } : {}),
package/dist/server.js CHANGED
@@ -627,6 +627,16 @@ export const createMoneyer = async (config, deps = {}) => {
627
627
  return fail('comment and h name different outputs');
628
628
  }
629
629
  const outputId = commentOutputId ?? askedOutputId;
630
+ // MONEYER_REQUIRE_COMMENT: refuse a quote that names no output at all,
631
+ // rather than minting a note keyed by the payment preimage. Off by
632
+ // default - LUD-25 line 80 still asks for that fallback - but the
633
+ // fallback note is only as safe as every routing hop's discretion, and
634
+ // a funding source that settles without a preimage has nothing to key
635
+ // one by. Refused here, before any invoice exists, so a wallet never
636
+ // pays for a quote this mint was always going to reject.
637
+ if (config.requireComment && outputId === null) {
638
+ return fail('a mint quote must name its output with a LUD-12 comment carrying hex(sha256(secret))');
639
+ }
630
640
  if (outputId !== null && store.outputIdInUse(outputId)) {
631
641
  return fail('Invalid or already spent k1.');
632
642
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgesworn/moneyer",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
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",