beignet 0.2.0 → 0.3.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.
Files changed (195) hide show
  1. package/README.md +284 -1
  2. package/dist/cli/beignet-node.js +1019 -47
  3. package/dist/cli/beignet-node.js.map +1 -1
  4. package/dist/cli/cli.js +1201 -16
  5. package/dist/cli/cli.js.map +1 -1
  6. package/dist/cli/config.js +21 -1
  7. package/dist/cli/config.js.map +1 -1
  8. package/dist/cli/daemon.js +342 -16
  9. package/dist/cli/daemon.js.map +1 -1
  10. package/dist/cli/errors.js +2 -0
  11. package/dist/cli/errors.js.map +1 -1
  12. package/dist/cli/openapi.js +1207 -14
  13. package/dist/cli/openapi.js.map +1 -1
  14. package/dist/cli/restore.js +89 -0
  15. package/dist/cli/restore.js.map +1 -0
  16. package/dist/cli/wallet-storage.js +34 -0
  17. package/dist/cli/wallet-storage.js.map +1 -0
  18. package/dist/electrum/index.js +105 -10
  19. package/dist/electrum/index.js.map +1 -1
  20. package/dist/lightning/advisor/fee-tuner.js +42 -0
  21. package/dist/lightning/advisor/fee-tuner.js.map +1 -0
  22. package/dist/lightning/advisor/index.js +9 -1
  23. package/dist/lightning/advisor/index.js.map +1 -1
  24. package/dist/lightning/advisor/rebalance-planner.js +55 -0
  25. package/dist/lightning/advisor/rebalance-planner.js.map +1 -0
  26. package/dist/lightning/backup/index.js +18 -0
  27. package/dist/lightning/backup/index.js.map +1 -0
  28. package/dist/lightning/backup/scb.js +42 -0
  29. package/dist/lightning/backup/scb.js.map +1 -0
  30. package/dist/lightning/chain/chain-monitor.js +27 -0
  31. package/dist/lightning/chain/chain-monitor.js.map +1 -1
  32. package/dist/lightning/chain/output-resolver.js +55 -6
  33. package/dist/lightning/chain/output-resolver.js.map +1 -1
  34. package/dist/lightning/chain/types.js +1 -0
  35. package/dist/lightning/chain/types.js.map +1 -1
  36. package/dist/lightning/channel/channel-manager.js +64 -0
  37. package/dist/lightning/channel/channel-manager.js.map +1 -1
  38. package/dist/lightning/channel/channel-state.js.map +1 -1
  39. package/dist/lightning/channel/channel.js +124 -4
  40. package/dist/lightning/channel/channel.js.map +1 -1
  41. package/dist/lightning/channel/dual-funding.js +6 -5
  42. package/dist/lightning/channel/dual-funding.js.map +1 -1
  43. package/dist/lightning/channel/types.js +3 -1
  44. package/dist/lightning/channel/types.js.map +1 -1
  45. package/dist/lightning/channel/validation.js +3 -3
  46. package/dist/lightning/channel/validation.js.map +1 -1
  47. package/dist/lightning/crypto/index.js +1 -0
  48. package/dist/lightning/crypto/index.js.map +1 -1
  49. package/dist/lightning/crypto/message-signing.js +116 -0
  50. package/dist/lightning/crypto/message-signing.js.map +1 -0
  51. package/dist/lightning/crypto/xchacha20poly1305.js +77 -0
  52. package/dist/lightning/crypto/xchacha20poly1305.js.map +1 -0
  53. package/dist/lightning/features/flags.js +1 -0
  54. package/dist/lightning/features/flags.js.map +1 -1
  55. package/dist/lightning/gossip/messages.js +37 -1
  56. package/dist/lightning/gossip/messages.js.map +1 -1
  57. package/dist/lightning/index.js +2 -1
  58. package/dist/lightning/index.js.map +1 -1
  59. package/dist/lightning/keys/wallet-keys.js +1 -0
  60. package/dist/lightning/keys/wallet-keys.js.map +1 -1
  61. package/dist/lightning/message/index.js +1 -0
  62. package/dist/lightning/message/index.js.map +1 -1
  63. package/dist/lightning/message/peer-storage.js +43 -0
  64. package/dist/lightning/message/peer-storage.js.map +1 -0
  65. package/dist/lightning/message/types.js +2 -0
  66. package/dist/lightning/message/types.js.map +1 -1
  67. package/dist/lightning/node/lightning-node.js +1583 -235
  68. package/dist/lightning/node/lightning-node.js.map +1 -1
  69. package/dist/lightning/node/types.js +18 -1
  70. package/dist/lightning/node/types.js.map +1 -1
  71. package/dist/lightning/offer/offer-manager.js +2 -5
  72. package/dist/lightning/offer/offer-manager.js.map +1 -1
  73. package/dist/lightning/offer/tlv.js +3 -1
  74. package/dist/lightning/offer/tlv.js.map +1 -1
  75. package/dist/lightning/storage/encryption.js +67 -0
  76. package/dist/lightning/storage/encryption.js.map +1 -0
  77. package/dist/lightning/storage/index.js +1 -0
  78. package/dist/lightning/storage/index.js.map +1 -1
  79. package/dist/lightning/storage/serialization.js +6 -2
  80. package/dist/lightning/storage/serialization.js.map +1 -1
  81. package/dist/lightning/storage/sqlite-storage.js +387 -33
  82. package/dist/lightning/storage/sqlite-storage.js.map +1 -1
  83. package/dist/lightning/watchtower/blob.js +135 -0
  84. package/dist/lightning/watchtower/blob.js.map +1 -0
  85. package/dist/lightning/watchtower/index.js +55 -0
  86. package/dist/lightning/watchtower/index.js.map +1 -0
  87. package/dist/lightning/watchtower/justice.js +182 -0
  88. package/dist/lightning/watchtower/justice.js.map +1 -0
  89. package/dist/lightning/watchtower/tower-connection.js +208 -0
  90. package/dist/lightning/watchtower/tower-connection.js.map +1 -0
  91. package/dist/lightning/watchtower/types.js +3 -0
  92. package/dist/lightning/watchtower/types.js.map +1 -0
  93. package/dist/lightning/watchtower/watchtower-client.js +402 -0
  94. package/dist/lightning/watchtower/watchtower-client.js.map +1 -0
  95. package/dist/lightning/watchtower/wtwire.js +201 -0
  96. package/dist/lightning/watchtower/wtwire.js.map +1 -0
  97. package/dist/shapes/electrum.js +32 -1
  98. package/dist/shapes/electrum.js.map +1 -1
  99. package/dist/shapes/wallet.js +11 -1
  100. package/dist/shapes/wallet.js.map +1 -1
  101. package/dist/transaction/index.js +163 -15
  102. package/dist/transaction/index.js.map +1 -1
  103. package/dist/types/cli/beignet-node.d.ts +144 -3
  104. package/dist/types/cli/daemon.d.ts +2 -0
  105. package/dist/types/cli/errors.d.ts +2 -0
  106. package/dist/types/cli/restore.d.ts +10 -0
  107. package/dist/types/cli/types.d.ts +228 -1
  108. package/dist/types/cli/wallet-storage.d.ts +3 -0
  109. package/dist/types/electrum/index.d.ts +12 -0
  110. package/dist/types/electrum.js +1 -0
  111. package/dist/types/electrum.js.map +1 -1
  112. package/dist/types/lightning/advisor/fee-tuner.d.ts +20 -0
  113. package/dist/types/lightning/advisor/index.d.ts +4 -0
  114. package/dist/types/lightning/advisor/rebalance-planner.d.ts +12 -0
  115. package/dist/types/lightning/backup/index.d.ts +1 -0
  116. package/dist/types/lightning/backup/scb.d.ts +24 -0
  117. package/dist/types/lightning/chain/chain-monitor.d.ts +1 -0
  118. package/dist/types/lightning/chain/types.d.ts +1 -0
  119. package/dist/types/lightning/channel/channel-manager.d.ts +7 -0
  120. package/dist/types/lightning/channel/channel-state.d.ts +2 -0
  121. package/dist/types/lightning/channel/channel.d.ts +6 -0
  122. package/dist/types/lightning/channel/dual-funding.d.ts +2 -1
  123. package/dist/types/lightning/channel/types.d.ts +2 -0
  124. package/dist/types/lightning/channel/validation.d.ts +1 -1
  125. package/dist/types/lightning/crypto/index.d.ts +1 -0
  126. package/dist/types/lightning/crypto/message-signing.d.ts +9 -0
  127. package/dist/types/lightning/crypto/xchacha20poly1305.d.ts +3 -0
  128. package/dist/types/lightning/features/flags.d.ts +1 -0
  129. package/dist/types/lightning/gossip/messages.d.ts +4 -0
  130. package/dist/types/lightning/index.d.ts +1 -0
  131. package/dist/types/lightning/keys/wallet-keys.d.ts +2 -1
  132. package/dist/types/lightning/message/index.d.ts +1 -0
  133. package/dist/types/lightning/message/peer-storage.d.ts +9 -0
  134. package/dist/types/lightning/message/types.d.ts +2 -0
  135. package/dist/types/lightning/node/lightning-node.d.ts +116 -4
  136. package/dist/types/lightning/node/types.d.ts +62 -1
  137. package/dist/types/lightning/storage/encryption.d.ts +11 -0
  138. package/dist/types/lightning/storage/index.d.ts +1 -0
  139. package/dist/types/lightning/storage/serialization.d.ts +2 -0
  140. package/dist/types/lightning/storage/sqlite-storage.d.ts +43 -3
  141. package/dist/types/lightning/storage/types.d.ts +62 -0
  142. package/dist/types/lightning/watchtower/blob.d.ts +32 -0
  143. package/dist/types/lightning/watchtower/index.d.ts +9 -0
  144. package/dist/types/lightning/watchtower/justice.d.ts +29 -0
  145. package/dist/types/lightning/watchtower/tower-connection.d.ts +34 -0
  146. package/dist/types/lightning/watchtower/types.d.ts +44 -0
  147. package/dist/types/lightning/watchtower/watchtower-client.d.ts +67 -0
  148. package/dist/types/lightning/watchtower/wtwire.d.ts +90 -0
  149. package/dist/types/shapes/electrum.d.ts +3 -1
  150. package/dist/types/transaction/index.d.ts +6 -0
  151. package/dist/types/transaction.js.map +1 -1
  152. package/dist/types/types/electrum.d.ts +3 -2
  153. package/dist/types/types/transaction.d.ts +1 -1
  154. package/dist/types/types/wallet.d.ts +78 -4
  155. package/dist/types/utils/descriptors.d.ts +2 -0
  156. package/dist/types/utils/fees.d.ts +3 -0
  157. package/dist/types/utils/helpers.d.ts +3 -1
  158. package/dist/types/utils/index.d.ts +5 -0
  159. package/dist/types/utils/multisig.d.ts +29 -0
  160. package/dist/types/utils/transaction.d.ts +7 -0
  161. package/dist/types/utils/wallet-storage-encryption.d.ts +3 -0
  162. package/dist/types/utils/wallet.d.ts +1 -1
  163. package/dist/types/utils/watch-only.d.ts +12 -0
  164. package/dist/types/wallet/constants.d.ts +1 -0
  165. package/dist/types/wallet/index.d.ts +61 -4
  166. package/dist/types/wallet.js +3 -0
  167. package/dist/types/wallet.js.map +1 -1
  168. package/dist/utils/derivation-path.js +8 -7
  169. package/dist/utils/derivation-path.js.map +1 -1
  170. package/dist/utils/descriptors.js +56 -0
  171. package/dist/utils/descriptors.js.map +1 -0
  172. package/dist/utils/electrum.js +10 -2
  173. package/dist/utils/electrum.js.map +1 -1
  174. package/dist/utils/fees.js +19 -0
  175. package/dist/utils/fees.js.map +1 -0
  176. package/dist/utils/helpers.js +22 -5
  177. package/dist/utils/helpers.js.map +1 -1
  178. package/dist/utils/index.js +5 -0
  179. package/dist/utils/index.js.map +1 -1
  180. package/dist/utils/multisig.js +90 -0
  181. package/dist/utils/multisig.js.map +1 -0
  182. package/dist/utils/transaction.js +30 -3
  183. package/dist/utils/transaction.js.map +1 -1
  184. package/dist/utils/wallet-storage-encryption.js +45 -0
  185. package/dist/utils/wallet-storage-encryption.js.map +1 -0
  186. package/dist/utils/wallet.js +3 -1
  187. package/dist/utils/wallet.js.map +1 -1
  188. package/dist/utils/watch-only.js +102 -0
  189. package/dist/utils/watch-only.js.map +1 -0
  190. package/dist/wallet/constants.js +2 -1
  191. package/dist/wallet/constants.js.map +1 -1
  192. package/dist/wallet/index.js +787 -30
  193. package/dist/wallet/index.js.map +1 -1
  194. package/docs/ROADMAP.md +292 -0
  195. package/package.json +1 -1
package/dist/cli/cli.js CHANGED
@@ -25,9 +25,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  };
26
26
  Object.defineProperty(exports, "__esModule", { value: true });
27
27
  const http = __importStar(require("http"));
28
+ const fs = __importStar(require("fs"));
29
+ const nodePath = __importStar(require("path"));
28
30
  const helpers_1 = require("../utils/helpers");
29
31
  const config_1 = require("./config");
30
32
  const daemon_1 = require("./daemon");
33
+ const beignet_node_1 = require("./beignet-node");
34
+ const restore_1 = require("./restore");
35
+ const instance_lock_1 = require("./instance-lock");
31
36
  const args = process.argv.slice(2);
32
37
  const pretty = args.includes('--pretty');
33
38
  const filteredArgs = args.filter((a) => a !== '--pretty');
@@ -44,6 +49,14 @@ function parseFlag(name) {
44
49
  function hasFlag(name) {
45
50
  return filteredArgs.includes(name);
46
51
  }
52
+ function parseRepeatedFlag(name) {
53
+ const out = [];
54
+ for (let i = 0; i < filteredArgs.length - 1; i++) {
55
+ if (filteredArgs[i] === name)
56
+ out.push(filteredArgs[i + 1]);
57
+ }
58
+ return out;
59
+ }
47
60
  function getApiToken() {
48
61
  const flagToken = parseFlag('--api-token');
49
62
  if (flagToken)
@@ -118,9 +131,36 @@ async function main() {
118
131
  : '/transactions'));
119
132
  case 'utxos':
120
133
  return outputResult(await httpRequest('GET', '/utxos'));
134
+ case 'utxo':
135
+ return handleUtxo();
121
136
  case 'fee-estimates':
122
137
  return outputResult(await httpRequest('GET', '/fees/estimates'));
123
138
  case 'address':
139
+ if (filteredArgs[1] === 'validate') {
140
+ return outputResult(await httpRequest('POST', '/address/validate', {
141
+ address: filteredArgs[2]
142
+ }));
143
+ }
144
+ if (filteredArgs[1] === 'label') {
145
+ return outputResult(await httpRequest('POST', '/address/label', {
146
+ address: filteredArgs[2],
147
+ label: filteredArgs.slice(3).join(' ')
148
+ }));
149
+ }
150
+ if (filteredArgs[1] === 'labels') {
151
+ return outputResult(await httpRequest('GET', '/address/labels'));
152
+ }
153
+ if (hasFlag('--bip21')) {
154
+ const amountFlag = parseFlag('--amount');
155
+ const labelFlag = parseFlag('--label');
156
+ const messageFlag = parseFlag('--message');
157
+ return outputResult(await httpRequest('POST', '/address/new', {
158
+ bip21: true,
159
+ ...(amountFlag ? { amountSats: parseInt(amountFlag, 10) } : {}),
160
+ ...(labelFlag ? { label: labelFlag } : {}),
161
+ ...(messageFlag ? { message: messageFlag } : {})
162
+ }));
163
+ }
124
164
  return outputResult(await httpRequest('POST', '/address/new'));
125
165
  case 'mnemonic':
126
166
  return outputResult(await httpRequest('GET', '/mnemonic'));
@@ -129,6 +169,23 @@ async function main() {
129
169
  address: filteredArgs[1],
130
170
  amountSats: parseInt(filteredArgs[2], 10)
131
171
  }));
172
+ case 'send-max':
173
+ return outputResult(await httpRequest('POST', '/send-max', {
174
+ address: filteredArgs[1],
175
+ satsPerVbyte: filteredArgs[2]
176
+ ? parseInt(filteredArgs[2], 10)
177
+ : undefined
178
+ }));
179
+ case 'tx':
180
+ return handleTx();
181
+ case 'psbt':
182
+ return handlePsbt();
183
+ case 'consolidate':
184
+ return outputResult(await httpRequest('POST', '/consolidate', {
185
+ satsPerVbyte: filteredArgs[1]
186
+ ? parseInt(filteredArgs[1], 10)
187
+ : undefined
188
+ }));
132
189
  case 'peer':
133
190
  return handlePeer();
134
191
  case 'channel':
@@ -137,6 +194,30 @@ async function main() {
137
194
  return handleInvoice();
138
195
  case 'payment':
139
196
  return handlePayment();
197
+ case 'keysend':
198
+ return handleKeysend();
199
+ case 'forwards':
200
+ return handleForwards();
201
+ case 'graph':
202
+ return handleGraph();
203
+ case 'gossip':
204
+ return handleGossip();
205
+ case 'message':
206
+ return handleMessage();
207
+ case 'recover-fallback-funds':
208
+ return outputResult(await httpRequest('POST', '/recover-fallback-funds', {
209
+ feeRatePerVbyte: parseFlag('--fee-rate')
210
+ ? parseInt(parseFlag('--fee-rate'), 10)
211
+ : undefined
212
+ }));
213
+ case 'watchtower':
214
+ return handleWatchtower();
215
+ case 'route':
216
+ return handleRoute();
217
+ case 'rebalance':
218
+ return handleRebalance();
219
+ case 'advisor':
220
+ return handleAdvisor();
140
221
  case 'bootstrap':
141
222
  return handleBootstrap();
142
223
  case 'trusted-peer':
@@ -145,16 +226,38 @@ async function main() {
145
226
  return handleOffer();
146
227
  case 'health':
147
228
  return outputResult(await httpRequest('GET', '/health'));
229
+ case 'ready':
230
+ return outputResult(await httpRequest('GET', '/ready'));
148
231
  case 'readiness':
149
232
  return outputResult(await httpRequest('GET', '/readiness'));
150
233
  case 'metrics':
151
234
  return handleMetrics();
152
235
  case 'stats':
153
236
  return outputResult(await httpRequest('GET', filteredArgs[1] ? `/stats?window=${filteredArgs[1]}` : '/stats'));
237
+ case 'liquidity':
238
+ return outputResult(await httpRequest('GET', '/liquidity'));
239
+ case 'fees':
240
+ return outputResult(await httpRequest('GET', '/fees'));
241
+ case 'spend-limit':
242
+ return outputResult(await httpRequest('GET', '/spend-limit'));
243
+ case 'logs':
244
+ return handleLogs();
245
+ case 'can-send':
246
+ return outputResult(await httpRequest('GET', `/can-send?amountSats=${encodeURIComponent(filteredArgs[1] || '0')}`));
247
+ case 'can-receive':
248
+ return outputResult(await httpRequest('GET', `/can-receive?amountSats=${encodeURIComponent(filteredArgs[1] || '0')}`));
249
+ case 'wallet':
250
+ return handleWallet();
251
+ case 'node':
252
+ return handleNode();
253
+ case 'webhooks':
254
+ return handleWebhooks();
255
+ case 'queue':
256
+ return handleQueue();
154
257
  case 'backup':
155
- return outputResult(await httpRequest('POST', '/backup', {
156
- destPath: filteredArgs[1]
157
- }));
258
+ return handleBackup();
259
+ case 'restore':
260
+ return handleRestore();
158
261
  default:
159
262
  output({
160
263
  ok: false,
@@ -222,6 +325,10 @@ async function handleStart() {
222
325
  cliFlags.daemonHost = hostFlag;
223
326
  if (hasFlag('--anchors'))
224
327
  cliFlags.preferAnchors = true;
328
+ if (hasFlag('--large-channels'))
329
+ cliFlags.largeChannels = true;
330
+ if (hasFlag('--htlc-events'))
331
+ cliFlags.htlcEvents = true;
225
332
  const apiTokenFlag = parseFlag('--api-token');
226
333
  if (apiTokenFlag)
227
334
  cliFlags.apiToken = apiTokenFlag;
@@ -249,6 +356,13 @@ async function handleStart() {
249
356
  .split(',')
250
357
  .map((a) => a.trim())
251
358
  .filter((a) => a.length > 0);
359
+ const watchtowerFlags = parseRepeatedFlag('--watchtower');
360
+ if (watchtowerFlags.length > 0)
361
+ cliFlags.watchtowers = watchtowerFlags;
362
+ const feeSourceFlag = parseFlag('--fee-source');
363
+ if (feeSourceFlag)
364
+ cliFlags.feeEstimationSource =
365
+ feeSourceFlag;
252
366
  const config = (0, config_1.resolveConfig)(cliFlags);
253
367
  if (!config.mnemonic) {
254
368
  output({
@@ -273,10 +387,12 @@ async function handleStart() {
273
387
  electrumPort: config.electrumPort,
274
388
  electrumTls: config.electrumTls,
275
389
  electrumServers: config.electrumServers,
390
+ feeEstimationSource: config.feeEstimationSource,
276
391
  listenPort: config.listenPort,
277
392
  daemonPort,
278
393
  daemonHost: config.daemonHost,
279
394
  preferAnchors: config.preferAnchors,
395
+ largeChannels: config.largeChannels,
280
396
  apiToken: config.apiToken,
281
397
  backupPath: config.backupPath,
282
398
  backupIntervalMs: config.backupIntervalMs,
@@ -284,7 +400,9 @@ async function handleStart() {
284
400
  tlsCert: config.tlsCert,
285
401
  tlsKey: config.tlsKey,
286
402
  torProxy: config.torProxy,
287
- announceAddresses: config.announceAddresses
403
+ announceAddresses: config.announceAddresses,
404
+ watchtowers: config.watchtowers,
405
+ htlcEvents: config.htlcEvents
288
406
  });
289
407
  (0, config_1.writePidFile)(process.pid, daemonPort);
290
408
  output({
@@ -321,15 +439,96 @@ async function handleStop() {
321
439
  output({ ok: false, error: { code: 'STOP_FAILED', message: msg } });
322
440
  }
323
441
  }
442
+ async function handleTx() {
443
+ const sub = filteredArgs[1];
444
+ switch (sub) {
445
+ case 'bump-fee':
446
+ return outputResult(await httpRequest('POST', '/tx/bump-fee', {
447
+ txid: filteredArgs[2],
448
+ satsPerVbyte: filteredArgs[3]
449
+ ? parseInt(filteredArgs[3], 10)
450
+ : undefined
451
+ }));
452
+ case 'boost':
453
+ return outputResult(await httpRequest('POST', '/tx/boost', {
454
+ txid: filteredArgs[2],
455
+ satsPerVbyte: filteredArgs[3]
456
+ ? parseInt(filteredArgs[3], 10)
457
+ : undefined
458
+ }));
459
+ case 'boostable':
460
+ return outputResult(await httpRequest('GET', '/transactions/boostable'));
461
+ default:
462
+ output({
463
+ ok: false,
464
+ error: {
465
+ code: 'UNKNOWN_COMMAND',
466
+ message: 'Usage: beignet tx [bump-fee <txid> <satsPerVbyte>|boost <txid> [satsPerVbyte]|boostable]'
467
+ }
468
+ });
469
+ process.exitCode = 1;
470
+ }
471
+ }
472
+ function readPsbtArg(arg) {
473
+ if (!arg)
474
+ return undefined;
475
+ try {
476
+ if (fs.existsSync(arg) && fs.statSync(arg).isFile()) {
477
+ return fs.readFileSync(arg, 'utf8').trim();
478
+ }
479
+ }
480
+ catch {
481
+ }
482
+ return arg;
483
+ }
484
+ async function handlePsbt() {
485
+ const sub = filteredArgs[1];
486
+ switch (sub) {
487
+ case 'build':
488
+ return outputResult(await httpRequest('POST', '/psbt/build', {
489
+ outputs: [
490
+ {
491
+ address: filteredArgs[2],
492
+ amountSats: parseInt(filteredArgs[3], 10)
493
+ }
494
+ ],
495
+ satsPerVbyte: filteredArgs[4]
496
+ ? parseInt(filteredArgs[4], 10)
497
+ : undefined
498
+ }));
499
+ case 'import-signed':
500
+ return outputResult(await httpRequest('POST', '/psbt/import-signed', {
501
+ psbtBase64: readPsbtArg(filteredArgs[2])
502
+ }));
503
+ case 'combine':
504
+ return outputResult(await httpRequest('POST', '/psbt/combine', {
505
+ psbts: filteredArgs
506
+ .slice(2)
507
+ .map((arg) => readPsbtArg(arg))
508
+ .filter((psbt) => !!psbt)
509
+ }));
510
+ default:
511
+ output({
512
+ ok: false,
513
+ error: {
514
+ code: 'UNKNOWN_COMMAND',
515
+ message: 'Usage: beignet psbt [build <address> <sats> [satsPerVbyte]|import-signed <psbtBase64|file>|combine <psbt|file> <psbt|file> ...]'
516
+ }
517
+ });
518
+ process.exitCode = 1;
519
+ }
520
+ }
324
521
  async function handlePeer() {
325
522
  const sub = filteredArgs[1];
326
523
  switch (sub) {
327
524
  case 'connect':
328
- return outputResult(await httpRequest('POST', '/peer/connect', {
329
- pubkey: filteredArgs[2],
330
- host: filteredArgs[3],
331
- port: parseInt(filteredArgs[4], 10)
332
- }));
525
+ return outputResult(await httpRequest('POST', '/peer/connect', filteredArgs[3] !== undefined
526
+ ? {
527
+ pubkey: filteredArgs[2],
528
+ host: filteredArgs[3],
529
+ port: parseInt(filteredArgs[4], 10)
530
+ }
531
+ : { pubkey: filteredArgs[2] }));
333
532
  case 'disconnect':
334
533
  return outputResult(await httpRequest('POST', '/peer/disconnect', {
335
534
  pubkey: filteredArgs[2]
@@ -399,12 +598,90 @@ async function handleChannel() {
399
598
  count: parseInt(filteredArgs[2], 10),
400
599
  satsPerChannel: parseInt(filteredArgs[3], 10)
401
600
  }));
601
+ case 'diagnostics':
602
+ return outputResult(await httpRequest('GET', `/channel/diagnostics?channelId=${encodeURIComponent(filteredArgs[2] || '')}`));
603
+ case 'health':
604
+ return outputResult(await httpRequest('GET', `/channel/health?channelId=${encodeURIComponent(filteredArgs[2] || '')}`));
605
+ case 'policy':
606
+ return outputResult(await httpRequest('GET', `/channel/policy?channelId=${encodeURIComponent(filteredArgs[2] || '')}`));
607
+ case 'suggestions':
608
+ return outputResult(await httpRequest('GET', filteredArgs[2]
609
+ ? `/channel/suggestions?count=${encodeURIComponent(filteredArgs[2])}`
610
+ : '/channel/suggestions'));
611
+ case 'ready':
612
+ return outputResult(await httpRequest('GET', '/channels/ready'));
613
+ case 'connect-and-open':
614
+ return outputResult(await httpRequest('POST', '/channel/connect-and-open', {
615
+ pubkey: filteredArgs[2],
616
+ host: filteredArgs[3],
617
+ port: filteredArgs[4] ? parseInt(filteredArgs[4], 10) : undefined,
618
+ amountSats: filteredArgs[5]
619
+ ? parseInt(filteredArgs[5], 10)
620
+ : undefined,
621
+ pushSats: filteredArgs[6] ? parseInt(filteredArgs[6], 10) : undefined
622
+ }));
623
+ case 'open-and-wait': {
624
+ const timeout = parseFlag('--timeout');
625
+ return outputResult(await httpRequest('POST', '/channel/open-and-wait', {
626
+ pubkey: filteredArgs[2],
627
+ amountSats: filteredArgs[3]
628
+ ? parseInt(filteredArgs[3], 10)
629
+ : undefined,
630
+ pushSats: filteredArgs[4] ? parseInt(filteredArgs[4], 10) : undefined,
631
+ timeoutMs: timeout !== undefined ? parseInt(timeout, 10) : undefined
632
+ }));
633
+ }
634
+ case 'wait-ready': {
635
+ const timeout = parseFlag('--timeout');
636
+ return outputResult(await httpRequest('POST', '/channel/wait-ready', {
637
+ channelId: filteredArgs[2],
638
+ timeoutMs: timeout !== undefined ? parseInt(timeout, 10) : undefined
639
+ }));
640
+ }
641
+ case 'update-commitment-feerate':
642
+ return outputResult(await httpRequest('POST', '/channel/update-commitment-feerate', {
643
+ channelId: filteredArgs[2],
644
+ feeratePerKw: filteredArgs[3]
645
+ ? parseInt(filteredArgs[3], 10)
646
+ : undefined
647
+ }));
648
+ case 'update-policy': {
649
+ const target = filteredArgs[2];
650
+ if (!target) {
651
+ output({
652
+ ok: false,
653
+ error: {
654
+ code: 'INVALID_PARAMS',
655
+ message: 'Usage: beignet channel update-policy <channelId|all> [--base-fee-msat N] [--ppm N] [--cltv-delta N] [--htlc-min-msat N] [--htlc-max-msat N]'
656
+ }
657
+ });
658
+ process.exitCode = 1;
659
+ return;
660
+ }
661
+ const body = target === 'all' ? { all: true } : { channelId: target };
662
+ const baseFee = parseFlag('--base-fee-msat');
663
+ if (baseFee !== undefined)
664
+ body.feeBaseMsat = parseInt(baseFee, 10);
665
+ const ppm = parseFlag('--ppm');
666
+ if (ppm !== undefined)
667
+ body.feeProportionalMillionths = parseInt(ppm, 10);
668
+ const cltvDelta = parseFlag('--cltv-delta');
669
+ if (cltvDelta !== undefined)
670
+ body.cltvExpiryDelta = parseInt(cltvDelta, 10);
671
+ const htlcMin = parseFlag('--htlc-min-msat');
672
+ if (htlcMin !== undefined)
673
+ body.htlcMinimumMsat = htlcMin;
674
+ const htlcMax = parseFlag('--htlc-max-msat');
675
+ if (htlcMax !== undefined)
676
+ body.htlcMaximumMsat = htlcMax;
677
+ return outputResult(await httpRequest('POST', '/channel/update-policy', body));
678
+ }
402
679
  default:
403
680
  output({
404
681
  ok: false,
405
682
  error: {
406
683
  code: 'UNKNOWN_COMMAND',
407
- message: 'Usage: beignet channel [open|open-zeroconf|open-v2|close|forceclose|splice-in|splice-out|ensure-minimum|list|get]'
684
+ message: 'Usage: beignet channel [open|open-zeroconf|open-v2|open-and-wait|connect-and-open|close|forceclose|splice-in|splice-out|ensure-minimum|update-policy|update-commitment-feerate|policy|diagnostics|health|suggestions|wait-ready|ready|list|get]'
408
685
  }
409
686
  });
410
687
  process.exitCode = 1;
@@ -418,14 +695,79 @@ async function handleInvoice() {
418
695
  amountSats: parseInt(filteredArgs[2], 10),
419
696
  description: filteredArgs[3] || ''
420
697
  }));
698
+ case 'create-hold': {
699
+ const paymentHash = filteredArgs[2];
700
+ if (!paymentHash) {
701
+ output({
702
+ ok: false,
703
+ error: {
704
+ code: 'INVALID_PARAMS',
705
+ message: 'Usage: beignet invoice create-hold <paymentHash> [amountSats] [description] [--expiry secs]'
706
+ }
707
+ });
708
+ process.exitCode = 1;
709
+ return;
710
+ }
711
+ const expiryFlag = parseFlag('--expiry');
712
+ return outputResult(await httpRequest('POST', '/invoice/create-hold', {
713
+ paymentHash,
714
+ amountSats: filteredArgs[3]
715
+ ? parseInt(filteredArgs[3], 10)
716
+ : undefined,
717
+ description: filteredArgs[4] || '',
718
+ expiry: expiryFlag ? parseInt(expiryFlag, 10) : undefined
719
+ }));
720
+ }
721
+ case 'settle-hold':
722
+ return outputResult(await httpRequest('POST', '/invoice/settle-hold', {
723
+ preimage: filteredArgs[2]
724
+ }));
725
+ case 'cancel-hold':
726
+ return outputResult(await httpRequest('POST', '/invoice/cancel-hold', {
727
+ paymentHash: filteredArgs[2]
728
+ }));
729
+ case 'held':
730
+ return outputResult(await httpRequest('GET', '/invoices/held'));
421
731
  case 'decode':
422
732
  return outputResult(await httpRequest('POST', '/invoice/decode', {
423
733
  bolt11: filteredArgs[2]
424
734
  }));
735
+ case 'validate':
736
+ return outputResult(await httpRequest('POST', '/invoice/validate', {
737
+ bolt11: filteredArgs[2],
738
+ amountSats: filteredArgs[3]
739
+ ? parseInt(filteredArgs[3], 10)
740
+ : undefined
741
+ }));
742
+ case 'get':
743
+ return outputResult(await httpRequest('GET', `/invoice?paymentHash=${encodeURIComponent(filteredArgs[2] || '')}`));
425
744
  case 'pay':
426
745
  return outputResult(await httpRequest('POST', '/invoice/pay', {
427
746
  bolt11: filteredArgs[2]
428
747
  }));
748
+ case 'pay-safe':
749
+ return outputResult(await httpRequest('POST', '/invoice/pay-safe', {
750
+ bolt11: filteredArgs[2],
751
+ maxFeeSats: parseFlag('--max-fee')
752
+ ? parseInt(parseFlag('--max-fee'), 10)
753
+ : undefined,
754
+ amountSats: parseFlag('--amount')
755
+ ? parseInt(parseFlag('--amount'), 10)
756
+ : undefined,
757
+ timeoutMs: parseFlag('--timeout')
758
+ ? parseInt(parseFlag('--timeout'), 10)
759
+ : undefined
760
+ }));
761
+ case 'pay-async':
762
+ return outputResult(await httpRequest('POST', '/invoice/pay-async', {
763
+ bolt11: filteredArgs[2],
764
+ maxFeeSats: parseFlag('--max-fee')
765
+ ? parseInt(parseFlag('--max-fee'), 10)
766
+ : undefined,
767
+ amountSats: parseFlag('--amount')
768
+ ? parseInt(parseFlag('--amount'), 10)
769
+ : undefined
770
+ }));
429
771
  case 'pay-retry':
430
772
  return outputResult(await httpRequest('POST', '/invoice/pay-retry', {
431
773
  bolt11: filteredArgs[2],
@@ -446,7 +788,7 @@ async function handleInvoice() {
446
788
  ok: false,
447
789
  error: {
448
790
  code: 'UNKNOWN_COMMAND',
449
- message: 'Usage: beignet invoice [create|decode|pay|pay-retry|list]'
791
+ message: 'Usage: beignet invoice [create|create-hold|settle-hold|cancel-hold|held|decode|validate|get|pay|pay-safe|pay-async|pay-retry|list]'
450
792
  }
451
793
  });
452
794
  process.exitCode = 1;
@@ -459,12 +801,520 @@ async function handlePayment() {
459
801
  return outputResult(await httpRequest('GET', '/payments'));
460
802
  case 'get':
461
803
  return outputResult(await httpRequest('GET', `/payment?paymentHash=${encodeURIComponent(filteredArgs[2] || '')}`));
804
+ case 'send-to-route': {
805
+ const paymentHash = filteredArgs[2];
806
+ const routeArg = filteredArgs[3];
807
+ if (!paymentHash || !routeArg) {
808
+ output({
809
+ ok: false,
810
+ error: {
811
+ code: 'INVALID_PARAMS',
812
+ message: 'Usage: beignet payment send-to-route <paymentHash> <routeJson|routeFile> [--payment-secret <hex>]'
813
+ }
814
+ });
815
+ process.exitCode = 1;
816
+ return;
817
+ }
818
+ let routeStr = routeArg;
819
+ if (!routeArg.trimStart().startsWith('{')) {
820
+ try {
821
+ routeStr = fs.readFileSync(routeArg, 'utf8');
822
+ }
823
+ catch (err) {
824
+ output({
825
+ ok: false,
826
+ error: {
827
+ code: 'INVALID_PARAMS',
828
+ message: `Cannot read route file: ${err.message}`
829
+ }
830
+ });
831
+ process.exitCode = 1;
832
+ return;
833
+ }
834
+ }
835
+ let route;
836
+ try {
837
+ route = JSON.parse(routeStr);
838
+ }
839
+ catch {
840
+ output({
841
+ ok: false,
842
+ error: {
843
+ code: 'INVALID_PARAMS',
844
+ message: 'Route is not valid JSON'
845
+ }
846
+ });
847
+ process.exitCode = 1;
848
+ return;
849
+ }
850
+ const result = route && typeof route === 'object' && 'result' in route
851
+ ? route.result
852
+ : route;
853
+ return outputResult(await httpRequest('POST', '/payment/send-to-route', {
854
+ paymentHash,
855
+ route: { hops: result.hops },
856
+ paymentSecret: parseFlag('--payment-secret')
857
+ }));
858
+ }
859
+ case 'cancel':
860
+ return outputResult(await httpRequest('POST', '/payment/cancel', {
861
+ paymentHash: filteredArgs[2]
862
+ }));
863
+ case 'wait': {
864
+ const timeout = parseFlag('--timeout');
865
+ return outputResult(await httpRequest('POST', '/payment/wait', {
866
+ paymentHash: filteredArgs[2],
867
+ timeoutMs: timeout !== undefined ? parseInt(timeout, 10) : undefined
868
+ }));
869
+ }
870
+ case 'proof':
871
+ return outputResult(await httpRequest('GET', `/payment/proof?paymentHash=${encodeURIComponent(filteredArgs[2] || '')}`));
872
+ case 'verify-proof':
873
+ return outputResult(await httpRequest('GET', `/payment/verify-proof?paymentHash=${encodeURIComponent(filteredArgs[2] || '')}`));
874
+ case 'estimate':
875
+ return outputResult(await httpRequest('POST', '/payment/estimate', {
876
+ bolt11: filteredArgs[2],
877
+ amountSats: filteredArgs[3]
878
+ ? parseInt(filteredArgs[3], 10)
879
+ : undefined
880
+ }));
881
+ case 'metadata': {
882
+ const paymentHash = filteredArgs[2];
883
+ const metadataArg = filteredArgs[3];
884
+ if (!paymentHash || !metadataArg) {
885
+ output({
886
+ ok: false,
887
+ error: {
888
+ code: 'INVALID_PARAMS',
889
+ message: 'Usage: beignet payment metadata <paymentHash> \'{"key":"value"}\''
890
+ }
891
+ });
892
+ process.exitCode = 1;
893
+ return;
894
+ }
895
+ let metadata;
896
+ try {
897
+ metadata = JSON.parse(metadataArg);
898
+ }
899
+ catch {
900
+ output({
901
+ ok: false,
902
+ error: {
903
+ code: 'INVALID_PARAMS',
904
+ message: 'Metadata is not valid JSON'
905
+ }
906
+ });
907
+ process.exitCode = 1;
908
+ return;
909
+ }
910
+ return outputResult(await httpRequest('POST', '/payment/metadata', {
911
+ paymentHash,
912
+ metadata
913
+ }));
914
+ }
915
+ default:
916
+ output({
917
+ ok: false,
918
+ error: {
919
+ code: 'UNKNOWN_COMMAND',
920
+ message: 'Usage: beignet payment [list|get|cancel|wait|proof|verify-proof|estimate|metadata|send-to-route]'
921
+ }
922
+ });
923
+ process.exitCode = 1;
924
+ }
925
+ }
926
+ async function handleKeysend() {
927
+ const safe = filteredArgs[1] === 'safe';
928
+ const base = safe ? 2 : 1;
929
+ const pubkey = filteredArgs[base];
930
+ const sats = filteredArgs[base + 1];
931
+ if (!pubkey || !sats) {
932
+ output({
933
+ ok: false,
934
+ error: {
935
+ code: 'INVALID_PARAMS',
936
+ message: 'Usage: beignet keysend [safe] <pubkey> <sats> [--max-fee <sats>] [--timeout <ms>]'
937
+ }
938
+ });
939
+ process.exitCode = 1;
940
+ return;
941
+ }
942
+ const maxFee = parseFlag('--max-fee');
943
+ const timeout = parseFlag('--timeout');
944
+ return outputResult(await httpRequest('POST', safe ? '/keysend/safe' : '/keysend', {
945
+ pubkey,
946
+ amountSats: parseInt(sats, 10),
947
+ maxFeeSats: maxFee !== undefined ? parseInt(maxFee, 10) : undefined,
948
+ timeoutMs: timeout !== undefined ? parseInt(timeout, 10) : undefined
949
+ }));
950
+ }
951
+ async function handleLogs() {
952
+ const params = new URLSearchParams();
953
+ const category = parseFlag('--category');
954
+ if (category !== undefined)
955
+ params.set('category', category);
956
+ const since = parseFlag('--since');
957
+ if (since !== undefined)
958
+ params.set('since', since);
959
+ const limit = parseFlag('--limit');
960
+ if (limit !== undefined)
961
+ params.set('limit', limit);
962
+ const qs = params.toString();
963
+ return outputResult(await httpRequest('GET', qs ? `/logs?${qs}` : '/logs'));
964
+ }
965
+ async function handleWallet() {
966
+ const sub = filteredArgs[1];
967
+ switch (sub) {
968
+ case 'refresh':
969
+ return outputResult(await httpRequest('POST', '/wallet/refresh'));
970
+ case 'descriptors':
971
+ return outputResult(await httpRequest('GET', '/wallet/descriptors'));
972
+ default:
973
+ output({
974
+ ok: false,
975
+ error: {
976
+ code: 'UNKNOWN_COMMAND',
977
+ message: 'Usage: beignet wallet [refresh|descriptors]'
978
+ }
979
+ });
980
+ process.exitCode = 1;
981
+ }
982
+ }
983
+ async function handleUtxo() {
984
+ const sub = filteredArgs[1];
985
+ const outpoint = () => ({
986
+ txid: filteredArgs[2],
987
+ index: parseInt(filteredArgs[3], 10)
988
+ });
989
+ switch (sub) {
990
+ case 'freeze':
991
+ return outputResult(await httpRequest('POST', '/utxo/freeze', outpoint()));
992
+ case 'unfreeze':
993
+ return outputResult(await httpRequest('POST', '/utxo/unfreeze', outpoint()));
994
+ case 'frozen': {
995
+ const res = await httpRequest('GET', '/utxos');
996
+ if (res.ok && Array.isArray(res.result)) {
997
+ return output({
998
+ ok: true,
999
+ result: res.result.filter((u) => u.frozen)
1000
+ });
1001
+ }
1002
+ return outputResult(res);
1003
+ }
1004
+ default:
1005
+ output({
1006
+ ok: false,
1007
+ error: {
1008
+ code: 'UNKNOWN_COMMAND',
1009
+ message: 'Usage: beignet utxo [freeze <txid> <index>|unfreeze <txid> <index>|frozen]'
1010
+ }
1011
+ });
1012
+ process.exitCode = 1;
1013
+ }
1014
+ }
1015
+ async function handleNode() {
1016
+ const sub = filteredArgs[1];
1017
+ switch (sub) {
1018
+ case 'uri': {
1019
+ const host = parseFlag('--host');
1020
+ return outputResult(await httpRequest('GET', host !== undefined
1021
+ ? `/node/uri?host=${encodeURIComponent(host)}`
1022
+ : '/node/uri'));
1023
+ }
1024
+ case 'wait-ready': {
1025
+ const timeout = parseFlag('--timeout');
1026
+ return outputResult(await httpRequest('POST', '/node/wait-ready', {
1027
+ timeoutMs: timeout !== undefined ? parseInt(timeout, 10) : undefined
1028
+ }));
1029
+ }
1030
+ default:
1031
+ output({
1032
+ ok: false,
1033
+ error: {
1034
+ code: 'UNKNOWN_COMMAND',
1035
+ message: 'Usage: beignet node [uri [--host <addr>]|wait-ready [--timeout <ms>]]'
1036
+ }
1037
+ });
1038
+ process.exitCode = 1;
1039
+ }
1040
+ }
1041
+ async function handleWebhooks() {
1042
+ const sub = filteredArgs[1];
1043
+ switch (sub) {
1044
+ case 'register': {
1045
+ const url = filteredArgs[2];
1046
+ const events = filteredArgs[3];
1047
+ if (!url || !events) {
1048
+ output({
1049
+ ok: false,
1050
+ error: {
1051
+ code: 'INVALID_PARAMS',
1052
+ message: 'Usage: beignet webhooks register <url> <event,event,...|*> [--secret <secret>]'
1053
+ }
1054
+ });
1055
+ process.exitCode = 1;
1056
+ return;
1057
+ }
1058
+ return outputResult(await httpRequest('POST', '/webhooks/register', {
1059
+ url,
1060
+ events: events
1061
+ .split(',')
1062
+ .map((e) => e.trim())
1063
+ .filter((e) => e.length > 0),
1064
+ secret: parseFlag('--secret')
1065
+ }));
1066
+ }
1067
+ case 'unregister':
1068
+ return outputResult(await httpRequest('DELETE', '/webhooks/unregister', {
1069
+ id: filteredArgs[2]
1070
+ }));
1071
+ case 'list':
1072
+ return outputResult(await httpRequest('GET', '/webhooks'));
1073
+ default:
1074
+ output({
1075
+ ok: false,
1076
+ error: {
1077
+ code: 'UNKNOWN_COMMAND',
1078
+ message: 'Usage: beignet webhooks [register <url> <events>|unregister <id>|list]'
1079
+ }
1080
+ });
1081
+ process.exitCode = 1;
1082
+ }
1083
+ }
1084
+ async function handleQueue() {
1085
+ const sub = filteredArgs[1];
1086
+ switch (sub) {
1087
+ case 'add': {
1088
+ const bolt11 = filteredArgs[2];
1089
+ if (!bolt11) {
1090
+ output({
1091
+ ok: false,
1092
+ error: {
1093
+ code: 'INVALID_PARAMS',
1094
+ message: 'Usage: beignet queue add <bolt11> [--priority <1-10>] [--amount <sats>] [--max-fee <sats>]'
1095
+ }
1096
+ });
1097
+ process.exitCode = 1;
1098
+ return;
1099
+ }
1100
+ const priority = parseFlag('--priority');
1101
+ const amount = parseFlag('--amount');
1102
+ const maxFee = parseFlag('--max-fee');
1103
+ return outputResult(await httpRequest('POST', '/queue/add', {
1104
+ bolt11,
1105
+ priority: priority !== undefined ? parseInt(priority, 10) : undefined,
1106
+ amountSats: amount !== undefined ? parseInt(amount, 10) : undefined,
1107
+ maxFeeSats: maxFee !== undefined ? parseInt(maxFee, 10) : undefined
1108
+ }));
1109
+ }
1110
+ case 'cancel':
1111
+ return outputResult(await httpRequest('POST', '/queue/cancel', { id: filteredArgs[2] }));
1112
+ case 'list':
1113
+ return outputResult(await httpRequest('GET', '/queue'));
1114
+ default:
1115
+ output({
1116
+ ok: false,
1117
+ error: {
1118
+ code: 'UNKNOWN_COMMAND',
1119
+ message: 'Usage: beignet queue [add <bolt11>|cancel <id>|list]'
1120
+ }
1121
+ });
1122
+ process.exitCode = 1;
1123
+ }
1124
+ }
1125
+ async function handleGraph() {
1126
+ const sub = filteredArgs[1];
1127
+ switch (sub) {
1128
+ case 'info':
1129
+ return outputResult(await httpRequest('GET', '/graph/info'));
1130
+ case 'node':
1131
+ return outputResult(await httpRequest('GET', `/graph/node?pubkey=${encodeURIComponent(filteredArgs[2] || '')}`));
1132
+ case 'channel':
1133
+ return outputResult(await httpRequest('GET', `/graph/channel?scid=${encodeURIComponent(filteredArgs[2] || '')}`));
1134
+ case 'describe': {
1135
+ const params = new URLSearchParams();
1136
+ const limit = parseFlag('--limit');
1137
+ if (limit !== undefined)
1138
+ params.set('limit', limit);
1139
+ const offset = parseFlag('--offset');
1140
+ if (offset !== undefined)
1141
+ params.set('offset', offset);
1142
+ const qs = params.toString();
1143
+ return outputResult(await httpRequest('GET', qs ? `/graph/describe?${qs}` : '/graph/describe'));
1144
+ }
1145
+ default:
1146
+ output({
1147
+ ok: false,
1148
+ error: {
1149
+ code: 'UNKNOWN_COMMAND',
1150
+ message: 'Usage: beignet graph [info|node <pubkey>|channel <scid>|describe [--limit N] [--offset N]]'
1151
+ }
1152
+ });
1153
+ process.exitCode = 1;
1154
+ }
1155
+ }
1156
+ async function handleWatchtower() {
1157
+ const sub = filteredArgs[1];
1158
+ switch (sub) {
1159
+ case 'list':
1160
+ return outputResult(await httpRequest('GET', '/watchtowers'));
1161
+ case 'add': {
1162
+ const uri = filteredArgs[2];
1163
+ if (!uri) {
1164
+ output({
1165
+ ok: false,
1166
+ error: {
1167
+ code: 'INVALID_PARAMS',
1168
+ message: 'Usage: beignet watchtower add <pubkey@host:port>'
1169
+ }
1170
+ });
1171
+ process.exitCode = 1;
1172
+ return;
1173
+ }
1174
+ return outputResult(await httpRequest('POST', '/watchtower/add', { uri }));
1175
+ }
1176
+ case 'remove': {
1177
+ const uri = filteredArgs[2];
1178
+ if (!uri) {
1179
+ output({
1180
+ ok: false,
1181
+ error: {
1182
+ code: 'INVALID_PARAMS',
1183
+ message: 'Usage: beignet watchtower remove <pubkey@host:port>'
1184
+ }
1185
+ });
1186
+ process.exitCode = 1;
1187
+ return;
1188
+ }
1189
+ return outputResult(await httpRequest('DELETE', '/watchtower/remove', { uri }));
1190
+ }
1191
+ default:
1192
+ output({
1193
+ ok: false,
1194
+ error: {
1195
+ code: 'UNKNOWN_COMMAND',
1196
+ message: 'Usage: beignet watchtower [list|add <uri>|remove <uri>]'
1197
+ }
1198
+ });
1199
+ process.exitCode = 1;
1200
+ }
1201
+ }
1202
+ async function handleRoute() {
1203
+ const sub = filteredArgs[1];
1204
+ switch (sub) {
1205
+ case 'query': {
1206
+ const destination = filteredArgs[2];
1207
+ const sats = filteredArgs[3];
1208
+ if (!destination || !sats) {
1209
+ output({
1210
+ ok: false,
1211
+ error: {
1212
+ code: 'INVALID_PARAMS',
1213
+ message: 'Usage: beignet route query <destination> <sats> [--max-fee <sats>]'
1214
+ }
1215
+ });
1216
+ process.exitCode = 1;
1217
+ return;
1218
+ }
1219
+ const maxFee = parseFlag('--max-fee');
1220
+ return outputResult(await httpRequest('POST', '/route/query', {
1221
+ destination,
1222
+ amountSats: parseInt(sats, 10),
1223
+ maxFeeSats: maxFee !== undefined ? parseInt(maxFee, 10) : undefined
1224
+ }));
1225
+ }
1226
+ case 'estimate':
1227
+ return outputResult(await httpRequest('POST', '/route/estimate', {
1228
+ bolt11: filteredArgs[2],
1229
+ amountSats: filteredArgs[3]
1230
+ ? parseInt(filteredArgs[3], 10)
1231
+ : undefined
1232
+ }));
1233
+ case 'probe': {
1234
+ const destination = filteredArgs[2];
1235
+ const sats = filteredArgs[3];
1236
+ if (!destination || !sats) {
1237
+ output({
1238
+ ok: false,
1239
+ error: {
1240
+ code: 'INVALID_PARAMS',
1241
+ message: 'Usage: beignet route probe <destination> <sats>'
1242
+ }
1243
+ });
1244
+ process.exitCode = 1;
1245
+ return;
1246
+ }
1247
+ return outputResult(await httpRequest('POST', '/route/probe', {
1248
+ destination,
1249
+ amountSats: parseInt(sats, 10)
1250
+ }));
1251
+ }
1252
+ default:
1253
+ output({
1254
+ ok: false,
1255
+ error: {
1256
+ code: 'UNKNOWN_COMMAND',
1257
+ message: 'Usage: beignet route [query <destination> <sats>|estimate <bolt11> [sats]|probe <destination> <sats>]'
1258
+ }
1259
+ });
1260
+ process.exitCode = 1;
1261
+ }
1262
+ }
1263
+ async function handleForwards() {
1264
+ const params = new URLSearchParams();
1265
+ const since = parseFlag('--since');
1266
+ if (since !== undefined)
1267
+ params.set('since', since);
1268
+ if (filteredArgs[1] === 'summary') {
1269
+ const qs = params.toString();
1270
+ return outputResult(await httpRequest('GET', `/forwards/summary${qs ? `?${qs}` : ''}`));
1271
+ }
1272
+ const limit = parseFlag('--limit');
1273
+ if (limit !== undefined)
1274
+ params.set('limit', limit);
1275
+ const qs = params.toString();
1276
+ return outputResult(await httpRequest('GET', `/forwards${qs ? `?${qs}` : ''}`));
1277
+ }
1278
+ async function handleRebalance() {
1279
+ const fromChannelId = filteredArgs[1];
1280
+ const toChannelId = filteredArgs[2];
1281
+ const amountSats = filteredArgs[3];
1282
+ const maxFee = parseFlag('--max-fee');
1283
+ if (!fromChannelId || !toChannelId || !amountSats || maxFee === undefined) {
1284
+ output({
1285
+ ok: false,
1286
+ error: {
1287
+ code: 'INVALID_PARAMS',
1288
+ message: 'Usage: beignet rebalance <fromChannelId> <toChannelId> <amountSats> --max-fee <sats>'
1289
+ }
1290
+ });
1291
+ process.exitCode = 1;
1292
+ return;
1293
+ }
1294
+ return outputResult(await httpRequest('POST', '/rebalance', {
1295
+ fromChannelId,
1296
+ toChannelId,
1297
+ amountSats: parseInt(amountSats, 10),
1298
+ maxFeeSats: parseInt(maxFee, 10)
1299
+ }));
1300
+ }
1301
+ async function handleAdvisor() {
1302
+ const sub = filteredArgs[1];
1303
+ switch (sub) {
1304
+ case 'recommendations':
1305
+ return outputResult(await httpRequest('GET', '/advisor/recommendations'));
1306
+ case 'execute-rebalances': {
1307
+ const budget = parseFlag('--budget');
1308
+ return outputResult(await httpRequest('POST', '/advisor/execute-rebalances', {
1309
+ budgetSatsPerDay: budget !== undefined ? parseInt(budget, 10) : undefined
1310
+ }));
1311
+ }
462
1312
  default:
463
1313
  output({
464
1314
  ok: false,
465
1315
  error: {
466
1316
  code: 'UNKNOWN_COMMAND',
467
- message: 'Usage: beignet payment [list|get]'
1317
+ message: 'Usage: beignet advisor [recommendations|execute-rebalances [--budget <sats>]]'
468
1318
  }
469
1319
  });
470
1320
  process.exitCode = 1;
@@ -526,6 +1376,10 @@ async function handleOffer() {
526
1376
  }));
527
1377
  case 'list':
528
1378
  return outputResult(await httpRequest('GET', '/offers'));
1379
+ case 'decode':
1380
+ return outputResult(await httpRequest('POST', '/offer/decode', {
1381
+ offer: filteredArgs[2]
1382
+ }));
529
1383
  case 'pay':
530
1384
  return outputResult(await httpRequest('POST', '/offer/pay', {
531
1385
  offer: filteredArgs[2],
@@ -538,11 +1392,197 @@ async function handleOffer() {
538
1392
  ok: false,
539
1393
  error: {
540
1394
  code: 'UNKNOWN_COMMAND',
541
- message: 'Usage: beignet offer [create|list|pay]'
1395
+ message: 'Usage: beignet offer [create|list|decode|pay]'
1396
+ }
1397
+ });
1398
+ process.exitCode = 1;
1399
+ }
1400
+ }
1401
+ async function handleGossip() {
1402
+ const sub = filteredArgs[1];
1403
+ switch (sub) {
1404
+ case 'sync':
1405
+ return outputResult(await httpRequest('POST', '/gossip/sync', {
1406
+ pubkey: filteredArgs[2] || undefined
1407
+ }));
1408
+ case 'sync-rapid':
1409
+ return outputResult(await httpRequest('POST', '/gossip/sync-rapid'));
1410
+ default:
1411
+ output({
1412
+ ok: false,
1413
+ error: {
1414
+ code: 'UNKNOWN_COMMAND',
1415
+ message: 'Usage: beignet gossip [sync [pubkey]|sync-rapid]'
1416
+ }
1417
+ });
1418
+ process.exitCode = 1;
1419
+ }
1420
+ }
1421
+ async function handleMessage() {
1422
+ const sub = filteredArgs[1];
1423
+ switch (sub) {
1424
+ case 'sign':
1425
+ return outputResult(await httpRequest('POST', '/message/sign', {
1426
+ message: filteredArgs[2]
1427
+ }));
1428
+ case 'verify':
1429
+ return outputResult(await httpRequest('POST', '/message/verify', {
1430
+ message: filteredArgs[2],
1431
+ signature: filteredArgs[3]
1432
+ }));
1433
+ default:
1434
+ output({
1435
+ ok: false,
1436
+ error: {
1437
+ code: 'UNKNOWN_COMMAND',
1438
+ message: 'Usage: beignet message [sign <message>|verify <message> <signature>]'
1439
+ }
1440
+ });
1441
+ process.exitCode = 1;
1442
+ }
1443
+ }
1444
+ async function handleBackup() {
1445
+ const sub = filteredArgs[1];
1446
+ if (sub === 'trigger') {
1447
+ return outputResult(await httpRequest('POST', '/backup/trigger'));
1448
+ }
1449
+ if (sub === 'peer-retrieved') {
1450
+ return outputResult(await httpRequest('GET', '/backup/peer-retrieved'));
1451
+ }
1452
+ if (sub === 'scb') {
1453
+ const result = await httpRequest('GET', '/backup/scb');
1454
+ const destPath = filteredArgs[2];
1455
+ if (!result.ok || !destPath)
1456
+ return outputResult(result);
1457
+ const { encoded, channelCount } = result.result;
1458
+ fs.writeFileSync(destPath, encoded);
1459
+ return output({
1460
+ ok: true,
1461
+ result: { written: true, path: destPath, channelCount }
1462
+ });
1463
+ }
1464
+ if (!sub) {
1465
+ output({
1466
+ ok: false,
1467
+ error: {
1468
+ code: 'INVALID_PARAMS',
1469
+ message: 'Usage: beignet backup <destPath> | beignet backup scb [destPath] | beignet backup trigger'
1470
+ }
1471
+ });
1472
+ process.exitCode = 1;
1473
+ return;
1474
+ }
1475
+ return outputResult(await httpRequest('POST', '/backup', { destPath: sub }));
1476
+ }
1477
+ async function handleRestore() {
1478
+ const sub = filteredArgs[1];
1479
+ const file = filteredArgs[2];
1480
+ if (sub === 'scb') {
1481
+ if (!file) {
1482
+ output({
1483
+ ok: false,
1484
+ error: {
1485
+ code: 'INVALID_PARAMS',
1486
+ message: 'Usage: beignet restore scb <file>'
1487
+ }
1488
+ });
1489
+ process.exitCode = 1;
1490
+ return;
1491
+ }
1492
+ let encoded;
1493
+ try {
1494
+ encoded = fs.readFileSync(file, 'utf8').trim();
1495
+ }
1496
+ catch (err) {
1497
+ output({
1498
+ ok: false,
1499
+ error: {
1500
+ code: 'INVALID_PARAMS',
1501
+ message: `Cannot read SCB file: ${err.message}`
1502
+ }
1503
+ });
1504
+ process.exitCode = 1;
1505
+ return;
1506
+ }
1507
+ return outputResult(await httpRequest('POST', '/restore/scb', { encoded }));
1508
+ }
1509
+ if (sub === 'db') {
1510
+ if (!file) {
1511
+ output({
1512
+ ok: false,
1513
+ error: {
1514
+ code: 'INVALID_PARAMS',
1515
+ message: 'Usage: beignet restore db <backupFile>'
1516
+ }
1517
+ });
1518
+ process.exitCode = 1;
1519
+ return;
1520
+ }
1521
+ const config = (0, config_1.resolveConfig)({});
1522
+ if (!config.mnemonic) {
1523
+ output({
1524
+ ok: false,
1525
+ error: {
1526
+ code: 'NO_MNEMONIC',
1527
+ message: 'No mnemonic found. Run "beignet init" first or set BEIGNET_MNEMONIC (the restored DB is seed-encrypted and needs the same mnemonic).'
542
1528
  }
543
1529
  });
544
1530
  process.exitCode = 1;
1531
+ return;
1532
+ }
1533
+ const pidInfo = (0, config_1.readPidFile)();
1534
+ if (pidInfo) {
1535
+ try {
1536
+ process.kill(pidInfo.pid, 0);
1537
+ output({
1538
+ ok: false,
1539
+ error: {
1540
+ code: 'DAEMON_RUNNING',
1541
+ message: `Daemon is running (PID ${pidInfo.pid}). Stop it with 'beignet stop' before restoring the database.`
1542
+ }
1543
+ });
1544
+ process.exitCode = 1;
1545
+ return;
1546
+ }
1547
+ catch {
1548
+ }
1549
+ }
1550
+ const network = config.network || 'mainnet';
1551
+ const dataDir = config.dataDir || (0, beignet_node_1.defaultDataDirForMnemonic)(config.mnemonic);
1552
+ const dbPath = nodePath.join(dataDir, `${network}.db`);
1553
+ const lockPath = nodePath.join(dataDir, `${network}.lock`);
1554
+ try {
1555
+ fs.mkdirSync(dataDir, { recursive: true });
1556
+ const result = (0, restore_1.performDbRestore)(file, dbPath, lockPath);
1557
+ output({
1558
+ ok: true,
1559
+ result: {
1560
+ restored: true,
1561
+ dbPath: result.dbPath,
1562
+ preRestorePath: result.preRestorePath,
1563
+ network,
1564
+ note: 'DB is encrypted under the wallet seed; start the node with the same mnemonic.'
1565
+ }
1566
+ });
1567
+ }
1568
+ catch (err) {
1569
+ const code = err instanceof instance_lock_1.InstanceLockError ? 'DAEMON_RUNNING' : 'RESTORE_FAILED';
1570
+ output({
1571
+ ok: false,
1572
+ error: { code, message: err.message }
1573
+ });
1574
+ process.exitCode = 1;
1575
+ }
1576
+ return;
545
1577
  }
1578
+ output({
1579
+ ok: false,
1580
+ error: {
1581
+ code: 'UNKNOWN_COMMAND',
1582
+ message: 'Usage: beignet restore scb <file> | beignet restore db <backupFile>'
1583
+ }
1584
+ });
1585
+ process.exitCode = 1;
546
1586
  }
547
1587
  async function handleMetrics() {
548
1588
  const port = (0, config_1.getDaemonPort)();
@@ -584,18 +1624,68 @@ Info:
584
1624
  info Node info
585
1625
  balance On-chain + Lightning balance
586
1626
  address New receive address
1627
+ address --bip21 [--amount <sats>] [--label L] [--message M]
1628
+ New address as a BIP21 URI
1629
+ address validate <address> Validate a Bitcoin address
1630
+ address label <address> [label] Set a user label for an address
1631
+ (omit the label to clear it)
1632
+ address labels List all user address labels
587
1633
  mnemonic Show mnemonic
588
1634
  health Node health status
1635
+ ready Whether the node is operational
589
1636
  readiness Mainnet readiness checklist
590
1637
  metrics Prometheus-format metrics (text/plain)
591
1638
  stats [windowMs] Node statistics (optional time window)
1639
+ liquidity Liquidity snapshot + recommendations
1640
+ fees On-chain fee trend analysis
1641
+ spend-limit Combined LN + on-chain daily spend
1642
+ limit status (with breakdown)
1643
+ logs [--category C] [--since ts] [--limit n]
1644
+ Query the persistent action log
1645
+ can-send [sats] Check Lightning send capacity
1646
+ can-receive [sats] Check Lightning receive capacity
1647
+ node uri [--host <addr>] Node connection URI (pubkey@host:port)
1648
+ node wait-ready [--timeout ms] Block until the node is operational
592
1649
 
593
1650
  On-chain:
594
1651
  send <address> <sats> Send on-chain
1652
+ send-max <address> [satsPerVbyte] Sweep the whole on-chain balance
1653
+ tx bump-fee <txid> <satsPerVbyte> RBF an unconfirmed tx at a higher fee
1654
+ tx boost <txid> [satsPerVbyte] Fee-bump a tx (RBF when possible,
1655
+ else CPFP)
1656
+ tx boostable List unconfirmed txs eligible for
1657
+ RBF/CPFP
1658
+ consolidate [satsPerVbyte] Merge all UTXOs into one output at a
1659
+ fresh wallet address
1660
+ psbt build <address> <sats> [satsPerVbyte]
1661
+ Build an UNSIGNED PSBT for an external
1662
+ signer (hardware wallet)
1663
+ psbt import-signed <psbtBase64|file> Validate + finalize a signed PSBT;
1664
+ returns txid/txHex WITHOUT broadcast
1665
+ psbt combine <psbt|file> <psbt|file> Combine partially signed PSBT copies
595
1666
  transactions [limit] List on-chain transactions (newest first)
596
- utxos List wallet UTXOs
1667
+ utxos List wallet UTXOs (includes frozen flag)
1668
+ utxo freeze <txid> <index> Freeze a UTXO (excluded from coin
1669
+ selection until unfrozen)
1670
+ utxo unfreeze <txid> <index> Unfreeze a UTXO
1671
+ utxo frozen List frozen UTXOs
597
1672
  fee-estimates Current fee estimates (sats/vbyte)
1673
+ wallet refresh Re-sync the on-chain wallet
1674
+ wallet descriptors Export BIP 380 output descriptors
1675
+ (public keys only, never private)
1676
+ recover-fallback-funds [--fee-rate N] Sweep funding-key fallback UTXOs into
1677
+ the wallet
598
1678
  backup <destPath> Create database backup
1679
+ backup trigger Run the configured scheduled backup now
1680
+ backup scb [destPath] Export encrypted static channel backup
1681
+ backup peer-retrieved Show newest SCB returned by a peer
1682
+ (BOLT 1 peer storage)
1683
+ restore scb <file> Restore channels from an SCB (on-chain
1684
+ recovery only: peers force-close and
1685
+ funds are swept to the wallet)
1686
+ restore db <backupFile> Restore a database backup (full state;
1687
+ OFFLINE - stop the daemon first; needs
1688
+ the same mnemonic, DB is seed-encrypted)
599
1689
 
600
1690
  Peers:
601
1691
  peer connect <pubkey> <host> <port> Connect to peer
@@ -615,29 +1705,119 @@ Channels:
615
1705
  channel open <pubkey> <sats> [push] Open channel (auto-funded)
616
1706
  channel open-zeroconf <pk> <sats> [push] Open zero-conf channel
617
1707
  channel open-v2 <pubkey> <sats> [feerate] Open dual-funded v2 channel
1708
+ channel open-and-wait <pubkey> <sats> [push] [--timeout ms]
1709
+ Open channel + block until NORMAL
1710
+ channel connect-and-open <pubkey> <host> <port> <sats> [push]
1711
+ Connect to peer + open in one call
618
1712
  channel close <id> Cooperative close
619
1713
  channel forceclose <id> Force close
620
1714
  channel splice-in <id> <sats> <feerate> Add funds to channel
621
1715
  channel splice-out <id> <sats> <feerate> Withdraw funds from channel
622
1716
  channel ensure-minimum <count> <sats> Auto-open channels to minimum count
1717
+ channel update-policy <id|all> [--base-fee-msat N] [--ppm N] [--cltv-delta N]
1718
+ [--htlc-min-msat N] [--htlc-max-msat N]
1719
+ Set routing fee policy (channel_update)
1720
+ channel update-commitment-feerate <id> <feeratePerKw>
1721
+ Set COMMITMENT feerate (BOLT 2
1722
+ update_fee), not the routing policy
1723
+ channel policy <id> Effective routing policy for a channel
623
1724
  channel list List channels
624
- channel get <id> Channel details
1725
+ channel ready List channels in NORMAL state
1726
+ channel get <id> Channel details (includes routing policy)
1727
+ channel health <id> Channel health + liquidity warnings
1728
+ channel diagnostics <id> Routing-readiness diagnostics for a channel
1729
+ channel suggestions [count] Graph-based channel open suggestions
1730
+ channel wait-ready <id> [--timeout ms] Block until a channel reaches NORMAL
625
1731
 
626
1732
  Invoices & Payments:
627
1733
  invoice create <sats> [description] Create BOLT 11 invoice
1734
+ invoice create-hold <hash> [sats] [description] [--expiry secs]
1735
+ Create hold invoice for a payment hash
1736
+ you supply (keep the preimage; HTLCs
1737
+ park until settle-hold/cancel-hold)
1738
+ invoice settle-hold <preimage> Settle a parked hold invoice
1739
+ invoice cancel-hold <hash> Cancel a hold invoice (fails HTLCs back)
1740
+ invoice held List hold invoices + their state
628
1741
  invoice decode <bolt11> Decode invoice
1742
+ invoice validate <bolt11> [sats] Pre-flight checks: should this be paid?
1743
+ invoice get <hash> Details of an invoice we created
629
1744
  invoice pay <bolt11> Pay invoice (blocks until settled)
1745
+ invoice pay-safe <bolt11> [--max-fee N] [--amount N] [--timeout ms]
1746
+ Pay; resolves with status FAILED
1747
+ instead of erroring
1748
+ invoice pay-async <bolt11> [--max-fee N] [--amount N]
1749
+ Fire-and-forget pay; poll 'payment get'
630
1750
  invoice pay-retry <bolt11> [flags] Pay with exponential backoff retry
631
1751
  invoice list List created invoices
1752
+ keysend [safe] <pubkey> <sats> [--max-fee N] [--timeout ms]
1753
+ Spontaneous payment, no invoice needed
1754
+ ('safe' resolves FAILED, never errors)
632
1755
  payment list List payments
633
1756
  payment get <hash> Payment details
1757
+ payment cancel <hash> Cancel a pending outbound payment
1758
+ payment wait <hash> [--timeout ms] Block until a payment settles
1759
+ payment proof <hash> Cryptographic payment proof
1760
+ payment verify-proof <hash> Verify a stored payment proof
1761
+ payment estimate <bolt11> [sats] Success probability + fee estimate
1762
+ payment metadata <hash> <json> Attach key-value metadata to a payment
1763
+ payment send-to-route <hash> <route> Pay along an explicit route (inline
1764
+ JSON or a file with 'route query'
1765
+ output) [--payment-secret <hex>]
1766
+ queue add <bolt11> [--priority N] [--amount N] [--max-fee N]
1767
+ Enqueue a payment for ordered dispatch
1768
+ queue cancel <id> Cancel a queued payment
1769
+ queue list List the payment queue
1770
+
1771
+ Graph Queries:
1772
+ graph info Graph summary (node/channel counts)
1773
+ graph node <pubkey> Node announcement info + its channels
1774
+ graph channel <scid> Channel endpoints + both fee policies
1775
+ (scid: <block>x<tx>x<out> or hex)
1776
+ graph describe [--limit N] [--offset N] Paged channel dump (default 500)
1777
+ gossip sync [pubkey] Sync gossip from peers (or one peer)
1778
+ gossip sync-rapid Rapid Gossip Sync snapshot (mainnet)
1779
+ route query <destination> <sats> Compute a route without paying
1780
+ [--max-fee <sats>]
1781
+ route estimate <bolt11> [sats] Estimate route fee for an invoice
1782
+ route probe <destination> <sats> Probe route viability (no payment)
1783
+
1784
+ Routing:
1785
+ forwards [--since ts] [--limit n] List settled forwards (fees earned)
1786
+ forwards summary [--since ts] Forwarding totals (count, volume, fees)
1787
+ rebalance <fromId> <toId> <sats> --max-fee <sats>
1788
+ Circular rebalance between two of our
1789
+ channels (aborts if fee exceeds cap)
1790
+ advisor recommendations Liquidity analysis + rebalance plan
1791
+ advisor execute-rebalances [--budget <sats>]
1792
+ Run the advisor's rebalance plan under
1793
+ a per-day fee budget
1794
+
1795
+ Messages:
1796
+ message sign <message> Sign with the node key (LND-compatible)
1797
+ message verify <message> <signature> Recover + check the signer pubkey
1798
+ Watchtowers:
1799
+ watchtower list Per-tower session + backlog health
1800
+ watchtower add <pubkey@host:port> Add an LND altruist watchtower
1801
+ watchtower remove <pubkey@host:port> Remove a watchtower
634
1802
 
635
1803
  BOLT 12 Offers:
636
1804
  offer create <description> [amountSats] Create reusable offer
637
1805
  offer list List local offers
1806
+ offer decode <offer> Decode a BOLT 12 offer string
638
1807
  offer pay <offer> [amountSats] Pay a BOLT 12 offer
639
1808
 
1809
+ Webhooks (event push; see also GET /events SSE):
1810
+ webhooks register <url> <events> [--secret S]
1811
+ Register a callback URL; <events> is
1812
+ comma-separated (or '*' for all)
1813
+ webhooks unregister <id> Remove a webhook
1814
+ webhooks list List registered webhooks
1815
+
640
1816
  Start flags:
1817
+ --network <name> mainnet | testnet | signet | regtest
1818
+ --fee-source <src> Fee estimate source: electrum | http |
1819
+ auto (default: auto = Electrum first,
1820
+ HTTP fallback)
641
1821
  --port <N> HTTP daemon port (default: 2112)
642
1822
  --host <addr> HTTP daemon bind address (default: 127.0.0.1)
643
1823
  --daemon Run in background
@@ -645,7 +1825,9 @@ Start flags:
645
1825
  --api-token <token> API authentication token
646
1826
  --backup-path <path> Enable automated backups to path
647
1827
  --backup-interval <ms> Backup interval (default: 21600000 = 6h)
648
- --daily-spend-limit <sats> Daily spending limit in satoshis
1828
+ --daily-spend-limit <sats> Combined daily spending limit in sats
1829
+ (Lightning payments AND external
1830
+ on-chain sends share one budget)
649
1831
  --tls-cert <path> TLS certificate file (enables HTTPS)
650
1832
  --tls-key <path> TLS private key file (requires --tls-cert)
651
1833
  --tor-proxy <host:port> SOCKS5 proxy for outbound Lightning peer
@@ -653,6 +1835,9 @@ Start flags:
653
1835
  --announce-addr <addr[,addr...]> Addresses to advertise in node_announcement
654
1836
  (IPv4, [ipv6]:port, .onion v3, or hostname;
655
1837
  port defaults to 9735)
1838
+ --htlc-events Relay per-HTLC events (htlc:forwarded/
1839
+ fulfilled/failed) over SSE + webhooks
1840
+ (off by default: high volume on routers)
656
1841
 
657
1842
  Pay-retry flags:
658
1843
  --max-retries <N> Max retry attempts (default: 3)