beignet 0.24.0 → 0.24.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.
Files changed (85) hide show
  1. package/README.md +3 -1
  2. package/dist/cli/backup-destination.js +89 -0
  3. package/dist/cli/backup-destination.js.map +1 -0
  4. package/dist/cli/backup-mac.js +74 -0
  5. package/dist/cli/backup-mac.js.map +1 -0
  6. package/dist/cli/beignet-node.js +157 -28
  7. package/dist/cli/beignet-node.js.map +1 -1
  8. package/dist/cli/cli.js +24 -9
  9. package/dist/cli/cli.js.map +1 -1
  10. package/dist/cli/daemon.js +9 -3
  11. package/dist/cli/daemon.js.map +1 -1
  12. package/dist/cli/openapi.js +34 -9
  13. package/dist/cli/openapi.js.map +1 -1
  14. package/dist/cli/restore.js +44 -22
  15. package/dist/cli/restore.js.map +1 -1
  16. package/dist/lightning/channel/channel.js +37 -2
  17. package/dist/lightning/channel/channel.js.map +1 -1
  18. package/dist/lightning/channel/dual-funding.js +6 -0
  19. package/dist/lightning/channel/dual-funding.js.map +1 -1
  20. package/dist/lightning/channel/types.js +7 -1
  21. package/dist/lightning/channel/types.js.map +1 -1
  22. package/dist/lightning/message/channel-update.js +12 -4
  23. package/dist/lightning/message/channel-update.js.map +1 -1
  24. package/dist/lightning/message/interactive-tx.js +10 -10
  25. package/dist/lightning/message/interactive-tx.js.map +1 -1
  26. package/dist/lightning/message/splice.js +8 -14
  27. package/dist/lightning/message/splice.js.map +1 -1
  28. package/dist/lightning/node/lightning-node.js +246 -76
  29. package/dist/lightning/node/lightning-node.js.map +1 -1
  30. package/dist/lightning/node/types.js +4 -1
  31. package/dist/lightning/node/types.js.map +1 -1
  32. package/dist/lightning/offer/tlv.js +2 -1
  33. package/dist/lightning/offer/tlv.js.map +1 -1
  34. package/dist/lightning/recovery/assembly.js +6 -1
  35. package/dist/lightning/recovery/assembly.js.map +1 -1
  36. package/dist/lightning/recovery/guardian-client.js +2 -2
  37. package/dist/lightning/recovery/guardian-client.js.map +1 -1
  38. package/dist/lightning/recovery/guardian-host.js +18 -3
  39. package/dist/lightning/recovery/guardian-host.js.map +1 -1
  40. package/dist/lightning/recovery/guardian-proto.js +37 -1
  41. package/dist/lightning/recovery/guardian-proto.js.map +1 -1
  42. package/dist/lightning/recovery/guardian-replication.js +329 -7
  43. package/dist/lightning/recovery/guardian-replication.js.map +1 -1
  44. package/dist/lightning/recovery/guardian-rotation.js +9 -2
  45. package/dist/lightning/recovery/guardian-rotation.js.map +1 -1
  46. package/dist/lightning/recovery/guardian-store.js +104 -7
  47. package/dist/lightning/recovery/guardian-store.js.map +1 -1
  48. package/dist/lightning/recovery/guardian-wire.js +11 -1
  49. package/dist/lightning/recovery/guardian-wire.js.map +1 -1
  50. package/dist/lightning/recovery/guardian.js +225 -44
  51. package/dist/lightning/recovery/guardian.js.map +1 -1
  52. package/dist/lightning/recovery/journal.js +24 -1
  53. package/dist/lightning/recovery/journal.js.map +1 -1
  54. package/dist/lightning/recovery/restore-driver.js +16 -9
  55. package/dist/lightning/recovery/restore-driver.js.map +1 -1
  56. package/dist/lightning/storage/serialization.js +12 -0
  57. package/dist/lightning/storage/serialization.js.map +1 -1
  58. package/dist/lightning/wallet/wallet-funding-provider.js +42 -8
  59. package/dist/lightning/wallet/wallet-funding-provider.js.map +1 -1
  60. package/dist/types/cli/backup-destination.d.ts +6 -0
  61. package/dist/types/cli/backup-mac.d.ts +6 -0
  62. package/dist/types/cli/beignet-node.d.ts +4 -0
  63. package/dist/types/cli/restore.d.ts +8 -2
  64. package/dist/types/lightning/channel/channel.d.ts +1 -0
  65. package/dist/types/lightning/channel/types.d.ts +3 -0
  66. package/dist/types/lightning/node/lightning-node.d.ts +5 -0
  67. package/dist/types/lightning/node/types.d.ts +2 -0
  68. package/dist/types/lightning/recovery/guardian-client.d.ts +2 -2
  69. package/dist/types/lightning/recovery/guardian-host.d.ts +7 -0
  70. package/dist/types/lightning/recovery/guardian-replication.d.ts +15 -2
  71. package/dist/types/lightning/recovery/guardian-rotation.d.ts +4 -0
  72. package/dist/types/lightning/recovery/guardian-store.d.ts +17 -3
  73. package/dist/types/lightning/recovery/guardian-wire.d.ts +7 -0
  74. package/dist/types/lightning/recovery/guardian.d.ts +17 -2
  75. package/dist/types/lightning/recovery/journal.d.ts +2 -0
  76. package/dist/types/lightning/recovery/restore-driver.d.ts +1 -0
  77. package/dist/types/lightning/storage/serialization.d.ts +2 -0
  78. package/dist/types/lightning/wallet/wallet-funding-provider.d.ts +11 -0
  79. package/dist/types/wallet/index.d.ts +15 -0
  80. package/dist/wallet/index.js +49 -2
  81. package/dist/wallet/index.js.map +1 -1
  82. package/docs/AI_AGENT_GUIDE.md +5 -0
  83. package/docs/RECOVERY-GUARDIAN-WIRE.md +76 -15
  84. package/docs/RECOVERY-PROTOCOL.md +2 -0
  85. package/package.json +1 -1
package/README.md CHANGED
@@ -755,7 +755,9 @@ Recommended safeguards in production:
755
755
  - Cap exposure with `maxPaymentSats` and `dailySpendLimitSats`. Both count a
756
756
  payment's amount plus its routing-fee cap, so the fee cannot slip past them:
757
757
  the cap is `maxFeeSats`/`maxFeeMsat` when you pass one, and 1% of the
758
- amount (never below 50 sats) when you do not.
758
+ amount (never below 50 sats) when you do not. Both also cover external
759
+ on-chain sends (amount plus fee); see the Spending Limits section of
760
+ `src/cli/README.md`.
759
761
  - Call `validatePayment()` before every send.
760
762
  - Set `backupPath` for automated database backups, and keep an SCB (`beignet backup scb`).
761
763
  - Keep `~/.beignet` and the data directory owner-only. The CLI creates them `0700`/`0600` and tightens an older config on load; check them again after copying files between hosts.
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.resolveBackupDestination = void 0;
27
+ const fs = __importStar(require("fs"));
28
+ const path = __importStar(require("path"));
29
+ const backup_mac_1 = require("./backup-mac");
30
+ function canonicalPath(p) {
31
+ const resolved = path.resolve(p);
32
+ try {
33
+ return fs.realpathSync.native(resolved);
34
+ }
35
+ catch {
36
+ return path.join(fs.realpathSync.native(path.dirname(resolved)), path.basename(resolved));
37
+ }
38
+ }
39
+ function resolveBackupDestination(destPath, protectedPaths, overwrite) {
40
+ let target;
41
+ try {
42
+ target = canonicalPath(destPath);
43
+ }
44
+ catch {
45
+ return {
46
+ refusal: `Backup directory does not exist: ${path.dirname(path.resolve(destPath))}`
47
+ };
48
+ }
49
+ if (target !== target.trim()) {
50
+ return { refusal: 'Backup filename must not start or end with whitespace' };
51
+ }
52
+ const stat = fs.statSync(target, { throwIfNoEntry: false });
53
+ if (!stat && fs.lstatSync(target, { throwIfNoEntry: false })) {
54
+ return { refusal: `destPath is a broken symlink: ${target}` };
55
+ }
56
+ for (const p of protectedPaths) {
57
+ let own;
58
+ try {
59
+ own = canonicalPath(p);
60
+ }
61
+ catch {
62
+ own = path.resolve(p);
63
+ }
64
+ if (own === target) {
65
+ return { refusal: `Refusing to overwrite a daemon file: ${target}` };
66
+ }
67
+ }
68
+ if (stat && !stat.isFile()) {
69
+ return { refusal: `destPath is not a regular file: ${target}` };
70
+ }
71
+ if (stat && !overwrite) {
72
+ return {
73
+ refusal: `destPath already exists: ${target}. Pass overwrite: true to replace it`
74
+ };
75
+ }
76
+ const macPath = (0, backup_mac_1.backupMacPath)(target);
77
+ const macStat = fs.lstatSync(macPath, { throwIfNoEntry: false });
78
+ if (macStat && !macStat.isFile()) {
79
+ return { refusal: `MAC path is not a regular file: ${macPath}` };
80
+ }
81
+ if (macStat && !overwrite) {
82
+ return {
83
+ refusal: `MAC path already exists: ${macPath}. Pass overwrite: true to replace it`
84
+ };
85
+ }
86
+ return { path: target };
87
+ }
88
+ exports.resolveBackupDestination = resolveBackupDestination;
89
+ //# sourceMappingURL=backup-destination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-destination.js","sourceRoot":"","sources":["../../src/cli/backup-destination.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,uCAAyB;AACzB,2CAA6B;AAC7B,6CAA6C;AAS7C,SAAS,aAAa,CAAC,CAAS;IAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;KACxC;IAAC,MAAM;QACP,OAAO,IAAI,CAAC,IAAI,CACf,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAC9C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CACvB,CAAC;KACF;AACF,CAAC;AAMD,SAAgB,wBAAwB,CACvC,QAAgB,EAChB,cAAwB,EACxB,SAAkB;IAElB,IAAI,MAAc,CAAC;IACnB,IAAI;QACH,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;KACjC;IAAC,MAAM;QACP,OAAO;YACN,OAAO,EAAE,oCAAoC,IAAI,CAAC,OAAO,CACxD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CACtB,EAAE;SACH,CAAC;KACF;IAED,IAAI,MAAM,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE;QAC7B,OAAO,EAAE,OAAO,EAAE,uDAAuD,EAAE,CAAC;KAC5E;IACD,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;IAE5D,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE;QAC7D,OAAO,EAAE,OAAO,EAAE,iCAAiC,MAAM,EAAE,EAAE,CAAC;KAC9D;IACD,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE;QAC/B,IAAI,GAAW,CAAC;QAChB,IAAI;YACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;SACvB;QAAC,MAAM;YACP,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;SACtB;QACD,IAAI,GAAG,KAAK,MAAM,EAAE;YACnB,OAAO,EAAE,OAAO,EAAE,wCAAwC,MAAM,EAAE,EAAE,CAAC;SACrE;KACD;IACD,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE;QAC3B,OAAO,EAAE,OAAO,EAAE,mCAAmC,MAAM,EAAE,EAAE,CAAC;KAChE;IACD,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE;QACvB,OAAO;YACN,OAAO,EAAE,4BAA4B,MAAM,sCAAsC;SACjF,CAAC;KACF;IAED,MAAM,OAAO,GAAG,IAAA,0BAAa,EAAC,MAAM,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC;IACjE,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE;QACjC,OAAO,EAAE,OAAO,EAAE,mCAAmC,OAAO,EAAE,EAAE,CAAC;KACjE;IACD,IAAI,OAAO,IAAI,CAAC,SAAS,EAAE;QAC1B,OAAO;YACN,OAAO,EAAE,4BAA4B,OAAO,sCAAsC;SAClF,CAAC;KACF;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACzB,CAAC;AAvDD,4DAuDC"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.readBackupMac = exports.writeBackupMac = exports.fileMac = exports.deriveBackupMacKey = exports.backupMacPath = void 0;
27
+ const crypto_1 = require("crypto");
28
+ const fs = __importStar(require("fs"));
29
+ const encryption_1 = require("../lightning/storage/encryption");
30
+ const fs_utils_1 = require("./fs-utils");
31
+ const BACKUP_MAC_INFO = 'beignet-db-backup-mac-v1';
32
+ const BACKUP_MAC_PREFIX = 'beignet-db-mac-v1:';
33
+ function backupMacPath(backupPath) {
34
+ return `${backupPath}.hmac`;
35
+ }
36
+ exports.backupMacPath = backupMacPath;
37
+ function deriveBackupMacKey(seed) {
38
+ return (0, encryption_1.hkdfKey)(seed, BACKUP_MAC_INFO);
39
+ }
40
+ exports.deriveBackupMacKey = deriveBackupMacKey;
41
+ async function fileMac(key, filePath) {
42
+ const hmac = (0, crypto_1.createHmac)('sha256', key);
43
+ for await (const chunk of fs.createReadStream(filePath)) {
44
+ hmac.update(chunk);
45
+ }
46
+ return hmac.digest();
47
+ }
48
+ exports.fileMac = fileMac;
49
+ async function writeBackupMac(key, backupPath) {
50
+ const mac = await fileMac(key, backupPath);
51
+ (0, fs_utils_1.writeFileAtomic)(backupMacPath(backupPath), `${BACKUP_MAC_PREFIX}${mac.toString('hex')}\n`);
52
+ }
53
+ exports.writeBackupMac = writeBackupMac;
54
+ function readBackupMac(backupPath) {
55
+ const macPath = backupMacPath(backupPath);
56
+ let text;
57
+ try {
58
+ text = fs.readFileSync(macPath, 'utf8').trim();
59
+ }
60
+ catch (err) {
61
+ if (err.code === 'ENOENT')
62
+ return null;
63
+ throw err;
64
+ }
65
+ const hex = text.startsWith(BACKUP_MAC_PREFIX)
66
+ ? text.slice(BACKUP_MAC_PREFIX.length)
67
+ : '';
68
+ if (!/^[0-9a-f]{64}$/.test(hex)) {
69
+ throw new Error(`Malformed backup MAC file: ${macPath}`);
70
+ }
71
+ return Buffer.from(hex, 'hex');
72
+ }
73
+ exports.readBackupMac = readBackupMac;
74
+ //# sourceMappingURL=backup-mac.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup-mac.js","sourceRoot":"","sources":["../../src/cli/backup-mac.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,mCAAoC;AACpC,uCAAyB;AACzB,gEAA0D;AAC1D,yCAA6C;AAE7C,MAAM,eAAe,GAAG,0BAA0B,CAAC;AACnD,MAAM,iBAAiB,GAAG,oBAAoB,CAAC;AAG/C,SAAgB,aAAa,CAAC,UAAkB;IAC/C,OAAO,GAAG,UAAU,OAAO,CAAC;AAC7B,CAAC;AAFD,sCAEC;AAGD,SAAgB,kBAAkB,CAAC,IAAY;IAC9C,OAAO,IAAA,oBAAO,EAAC,IAAI,EAAE,eAAe,CAAC,CAAC;AACvC,CAAC;AAFD,gDAEC;AAGM,KAAK,UAAU,OAAO,CAAC,GAAW,EAAE,QAAgB;IAC1D,MAAM,IAAI,GAAG,IAAA,mBAAU,EAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE;QACxD,IAAI,CAAC,MAAM,CAAC,KAAe,CAAC,CAAC;KAC7B;IACD,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;AACtB,CAAC;AAND,0BAMC;AAGM,KAAK,UAAU,cAAc,CACnC,GAAW,EACX,UAAkB;IAElB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC3C,IAAA,0BAAe,EACd,aAAa,CAAC,UAAU,CAAC,EACzB,GAAG,iBAAiB,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAC9C,CAAC;AACH,CAAC;AATD,wCASC;AAMD,SAAgB,aAAa,CAAC,UAAkB;IAC/C,MAAM,OAAO,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAC1C,IAAI,IAAY,CAAC;IACjB,IAAI;QACH,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;KAC/C;IAAC,OAAO,GAAG,EAAE;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAClE,MAAM,GAAG,CAAC;KACV;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC;QAC7C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC;QACtC,CAAC,CAAC,EAAE,CAAC;IACN,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAChC,MAAM,IAAI,KAAK,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAC;KACzD;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAChC,CAAC;AAhBD,sCAgBC"}
@@ -42,6 +42,7 @@ const conversion_1 = require("../utils/conversion");
42
42
  const wallet_3 = require("../types/wallet");
43
43
  const wallet_storage_1 = require("./wallet-storage");
44
44
  const fs_utils_1 = require("./fs-utils");
45
+ const backup_mac_1 = require("./backup-mac");
45
46
  const node_storage_view_1 = require("./node-storage-view");
46
47
  const electrum_1 = require("../types/electrum");
47
48
  const lightning_node_1 = require("../lightning/node/lightning-node");
@@ -703,6 +704,7 @@ class BeignetNode extends events_1.EventEmitter {
703
704
  this._dailySpendResetTime = 0;
704
705
  this._pendingSpendSats = 0;
705
706
  this._asyncSpendClaims = new Map();
707
+ this._liveRebalanceChargeSats = 0;
706
708
  this._onchainSendLock = Promise.resolve();
707
709
  this._l402Credentials = new l402_1.MemoryL402CredentialStore();
708
710
  this._draining = false;
@@ -1241,7 +1243,10 @@ class BeignetNode extends events_1.EventEmitter {
1241
1243
  }
1242
1244
  : {}),
1243
1245
  peerStorageEnabled: this.peerStorageEnabled,
1244
- autoRebalance: opts.autoRebalance,
1246
+ autoRebalance: opts.autoRebalance && {
1247
+ ...opts.autoRebalance,
1248
+ enabled: false
1249
+ },
1245
1250
  autoTuneFees: opts.autoTuneFees,
1246
1251
  watchtowers: opts.watchtowers,
1247
1252
  recovery: this.recoveryNodeConfig
@@ -1710,6 +1715,16 @@ class BeignetNode extends events_1.EventEmitter {
1710
1715
  if (opts.maxPaymentSats !== undefined && opts.maxPaymentSats > 0) {
1711
1716
  this._maxPaymentSats = opts.maxPaymentSats;
1712
1717
  }
1718
+ if (opts.autoRebalance?.enabled === true) {
1719
+ this._autoRebalanceTimer = setInterval(() => {
1720
+ this.executeRebalances().catch((err) => {
1721
+ this.log('warn', 'Automatic rebalance failed', {
1722
+ error: err instanceof Error ? err.message : String(err)
1723
+ });
1724
+ });
1725
+ }, opts.autoRebalance.intervalMs ?? 3600000);
1726
+ this._autoRebalanceTimer.unref?.();
1727
+ }
1713
1728
  if (opts.autoBootstrap) {
1714
1729
  this.node.connectToSeeds().catch(() => {
1715
1730
  });
@@ -1901,13 +1916,18 @@ class BeignetNode extends events_1.EventEmitter {
1901
1916
  });
1902
1917
  }
1903
1918
  relayRecordTooLarge(event) {
1904
- if (event.type !== 'record:too-large')
1919
+ if (event.type !== 'record:too-large' &&
1920
+ event.type !== 'record:quota-refused') {
1905
1921
  return;
1906
- this.log('error', 'Recovery guardian refused an oversized record', {
1907
- detail: event.detail
1908
- });
1922
+ }
1923
+ const tooLarge = event.type === 'record:too-large';
1924
+ this.log('error', tooLarge
1925
+ ? 'Recovery guardian refused an oversized record'
1926
+ : 'Recovery guardian refused a record: its quota is full', { detail: event.detail });
1909
1927
  const data = {
1910
- code: 'RECOVERY_RECORD_TOO_LARGE',
1928
+ code: tooLarge
1929
+ ? 'RECOVERY_RECORD_TOO_LARGE'
1930
+ : 'RECOVERY_GUARDIAN_QUOTA_EXCEEDED',
1911
1931
  message: event.detail,
1912
1932
  timestamp: Date.now()
1913
1933
  };
@@ -2620,6 +2640,10 @@ class BeignetNode extends events_1.EventEmitter {
2620
2640
  clearInterval(this._fallbackRecoveryTimer);
2621
2641
  this._fallbackRecoveryTimer = undefined;
2622
2642
  }
2643
+ if (this._autoRebalanceTimer) {
2644
+ clearInterval(this._autoRebalanceTimer);
2645
+ this._autoRebalanceTimer = undefined;
2646
+ }
2623
2647
  this.paymentQueue?.removeAllListeners();
2624
2648
  this.node.destroy();
2625
2649
  try {
@@ -2831,6 +2855,10 @@ class BeignetNode extends events_1.EventEmitter {
2831
2855
  ...(0, recovery_1.bindGuardianSet)(incoming, { transportFor })
2832
2856
  },
2833
2857
  required: recovery_1.CRASH_V1_PROFILE.required,
2858
+ journalKeys: {
2859
+ masterKey: (0, recovery_1.deriveRecoveryMasterKey)(this.nodeSecret()),
2860
+ nodeId: (0, ecdh_1.getPublicKey)(this.nodeSecret())
2861
+ },
2834
2862
  onEvent: (event) => this.noteRotationEvent(event),
2835
2863
  onReplicationEvent: (event) => {
2836
2864
  this.log('debug', `Rotation replication: ${event.type}`, {
@@ -3348,6 +3376,7 @@ class BeignetNode extends events_1.EventEmitter {
3348
3376
  return run;
3349
3377
  }
3350
3378
  async sendOnchain(address, amountSats, satsPerVbyte) {
3379
+ this._checkMaxPayment(amountSats);
3351
3380
  this._checkSpendLimit(amountSats);
3352
3381
  return this._runOnchainSend(async () => {
3353
3382
  try {
@@ -3361,19 +3390,27 @@ class BeignetNode extends events_1.EventEmitter {
3361
3390
  if (result.isErr()) {
3362
3391
  throw new errors_1.BeignetError('SEND_FAILED', result.error.message);
3363
3392
  }
3364
- if (this._dailySpendLimitSats === undefined) {
3393
+ if (this._dailySpendLimitSats === undefined &&
3394
+ this._maxPaymentSats === undefined) {
3365
3395
  return await this._broadcastRawTx(result.value);
3366
3396
  }
3367
3397
  const totalSats = this._builtOnchainTotalSats(amountSats);
3398
+ this._checkMaxPayment(totalSats, amountSats, {
3399
+ name: 'on-chain fees',
3400
+ param: 'satsPerVbyte'
3401
+ });
3368
3402
  this._checkSpendLimit(totalSats);
3369
- this._pendingSpendSats += totalSats;
3403
+ const reserving = this._dailySpendLimitSats !== undefined;
3404
+ if (reserving)
3405
+ this._pendingSpendSats += totalSats;
3370
3406
  try {
3371
3407
  const info = await this._broadcastRawTx(result.value);
3372
3408
  this._recordSpend(totalSats, 'onchain');
3373
3409
  return info;
3374
3410
  }
3375
3411
  finally {
3376
- this._pendingSpendSats -= totalSats;
3412
+ if (reserving)
3413
+ this._pendingSpendSats -= totalSats;
3377
3414
  }
3378
3415
  }
3379
3416
  finally {
@@ -3601,24 +3638,29 @@ class BeignetNode extends events_1.EventEmitter {
3601
3638
  if (result.isErr()) {
3602
3639
  throw new errors_1.BeignetError('SEND_FAILED', result.error.message);
3603
3640
  }
3604
- if (this._dailySpendLimitSats === undefined) {
3641
+ if (this._dailySpendLimitSats === undefined &&
3642
+ this._maxPaymentSats === undefined) {
3605
3643
  return await this._broadcastRawTx(result.value);
3606
3644
  }
3607
3645
  const totalSats = this.wallet.transaction.getTransactionInputValue({
3608
3646
  inputs: this.wallet.transaction.data.inputs
3609
3647
  });
3610
3648
  if (totalSats <= 0) {
3611
- throw new errors_1.BeignetError('SPENDING_LIMIT_EXCEEDED', 'Unable to determine the swept amount for the daily spend limit check; refusing to send');
3649
+ throw new errors_1.BeignetError('SPENDING_LIMIT_EXCEEDED', 'Unable to determine the swept amount for the spend limit checks; refusing to send');
3612
3650
  }
3651
+ this._checkMaxPayment(totalSats);
3613
3652
  this._checkSpendLimit(totalSats);
3614
- this._pendingSpendSats += totalSats;
3653
+ const reserving = this._dailySpendLimitSats !== undefined;
3654
+ if (reserving)
3655
+ this._pendingSpendSats += totalSats;
3615
3656
  try {
3616
3657
  const info = await this._broadcastRawTx(result.value);
3617
3658
  this._recordSpend(totalSats, 'onchain');
3618
3659
  return info;
3619
3660
  }
3620
3661
  finally {
3621
- this._pendingSpendSats -= totalSats;
3662
+ if (reserving)
3663
+ this._pendingSpendSats -= totalSats;
3622
3664
  }
3623
3665
  }
3624
3666
  finally {
@@ -5097,6 +5139,10 @@ class BeignetNode extends events_1.EventEmitter {
5097
5139
  try {
5098
5140
  const quote = sender.quote(opts.request, sendOpts);
5099
5141
  costSats = Number(quote.amountSat + quote.maxTotalFeeSat);
5142
+ this._checkMaxPayment(costSats, Number(quote.amountSat), {
5143
+ name: 'fees',
5144
+ param: 'maxTotalFeeSat'
5145
+ });
5100
5146
  this._checkSpendLimit(costSats);
5101
5147
  }
5102
5148
  catch (err) {
@@ -5287,6 +5333,11 @@ class BeignetNode extends events_1.EventEmitter {
5287
5333
  this._dailySpentSats = 0;
5288
5334
  this._dailySpentLightningSats = 0;
5289
5335
  this._dailySpentOnchainSats = 0;
5336
+ if (this._dailySpendLimitSats !== undefined &&
5337
+ this._liveRebalanceChargeSats > 0) {
5338
+ this._dailySpentSats = this._liveRebalanceChargeSats;
5339
+ this._dailySpentLightningSats = this._liveRebalanceChargeSats;
5340
+ }
5290
5341
  if (persist)
5291
5342
  this._persistSpendState();
5292
5343
  }
@@ -5305,14 +5356,14 @@ class BeignetNode extends events_1.EventEmitter {
5305
5356
  throw new errors_1.BeignetError('SPENDING_LIMIT_EXCEEDED', `Daily spend limit exceeded. Limit: ${this._dailySpendLimitSats} sats, spent: ${this._dailySpentSats} sats, remaining: ${remaining} sats, requested: ${requested}`);
5306
5357
  }
5307
5358
  }
5308
- _checkMaxPayment(spendSats, amountSats = spendSats) {
5359
+ _checkMaxPayment(spendSats, amountSats = spendSats, fees = { name: 'routing fees', param: 'maxFeeSats' }) {
5309
5360
  if (this._maxPaymentSats === undefined)
5310
5361
  return;
5311
5362
  if (amountSats > this._maxPaymentSats) {
5312
5363
  throw new errors_1.BeignetError('SPENDING_LIMIT_EXCEEDED', `Payment amount ${amountSats} sats exceeds per-payment limit of ${this._maxPaymentSats} sats`);
5313
5364
  }
5314
5365
  if (spendSats > this._maxPaymentSats) {
5315
- throw new errors_1.BeignetError('SPENDING_LIMIT_EXCEEDED', `Payment amount ${amountSats} sats plus up to ${spendSats - amountSats} sats in routing fees exceeds per-payment limit of ${this._maxPaymentSats} sats; lower maxFeeSats or the amount`);
5366
+ throw new errors_1.BeignetError('SPENDING_LIMIT_EXCEEDED', `Payment amount ${amountSats} sats plus up to ${spendSats - amountSats} sats in ${fees.name} exceeds per-payment limit of ${this._maxPaymentSats} sats; lower ${fees.param} or the amount`);
5316
5367
  }
5317
5368
  }
5318
5369
  _admitLightningSpend(claimKey, payAmountMsat, maxFeeMsat) {
@@ -5337,6 +5388,14 @@ class BeignetNode extends events_1.EventEmitter {
5337
5388
  }
5338
5389
  this._persistSpendState();
5339
5390
  }
5391
+ _refundSpend(sats) {
5392
+ if (this._dailySpendLimitSats === undefined)
5393
+ return;
5394
+ this._resetDailySpendIfNeeded(false);
5395
+ this._dailySpentSats -= sats;
5396
+ this._dailySpentLightningSats -= sats;
5397
+ this._persistSpendState();
5398
+ }
5340
5399
  _openAsyncSpendClaim(paymentHashHex, amountSats) {
5341
5400
  if (this._dailySpendLimitSats === undefined)
5342
5401
  return undefined;
@@ -5667,8 +5726,9 @@ class BeignetNode extends events_1.EventEmitter {
5667
5726
  _builtOnchainTotalSats(amountSats) {
5668
5727
  const feeSats = this.wallet.transaction?.data?.fee;
5669
5728
  if (!Number.isFinite(feeSats) || feeSats < 0) {
5670
- if (this._dailySpendLimitSats !== undefined) {
5671
- throw new errors_1.BeignetError('SPENDING_LIMIT_EXCEEDED', 'Unable to determine the transaction fee for the daily spend limit check; refusing to send');
5729
+ if (this._dailySpendLimitSats !== undefined ||
5730
+ this._maxPaymentSats !== undefined) {
5731
+ throw new errors_1.BeignetError('SPENDING_LIMIT_EXCEEDED', 'Unable to determine the transaction fee for the spend limit checks; refusing to send');
5672
5732
  }
5673
5733
  return amountSats;
5674
5734
  }
@@ -6706,6 +6766,10 @@ class BeignetNode extends events_1.EventEmitter {
6706
6766
  destinationScriptLen: destinationScript.length
6707
6767
  }), feeratePerkw));
6708
6768
  externalSpendSats = amountSats + feeSats;
6769
+ this._checkMaxPayment(externalSpendSats, amountSats, {
6770
+ name: 'on-chain fees',
6771
+ param: 'feeratePerkw'
6772
+ });
6709
6773
  this._checkSpendLimit(externalSpendSats);
6710
6774
  }
6711
6775
  const result = fundingOrRefuse(() => this.node.spliceOut(idBuf, BigInt(amountSats), feeratePerkw, destinationScript));
@@ -7186,12 +7250,31 @@ class BeignetNode extends events_1.EventEmitter {
7186
7250
  throw new errors_1.BeignetError(errors_1.BeignetErrorCode.INVALID_PARAMS, 'amountSats must be a positive integer');
7187
7251
  if (!Number.isInteger(maxFeeSats) || maxFeeSats < 0)
7188
7252
  throw new errors_1.BeignetError(errors_1.BeignetErrorCode.INVALID_PARAMS, 'maxFeeSats must be a non-negative integer');
7189
- const result = await this.node.rebalanceChannel({
7190
- fromChannelId: Buffer.from(fromChannelId, 'hex'),
7191
- toChannelId: Buffer.from(toChannelId, 'hex'),
7192
- amountSats: BigInt(amountSats),
7193
- maxFeeSats: BigInt(maxFeeSats)
7194
- });
7253
+ this._checkDraining();
7254
+ this._checkSpendLimit(maxFeeSats);
7255
+ this._recordSpend(maxFeeSats);
7256
+ this._liveRebalanceChargeSats += maxFeeSats;
7257
+ let spentSats = maxFeeSats;
7258
+ let result;
7259
+ try {
7260
+ result = await this.node.rebalanceChannel({
7261
+ fromChannelId: Buffer.from(fromChannelId, 'hex'),
7262
+ toChannelId: Buffer.from(toChannelId, 'hex'),
7263
+ amountSats: BigInt(amountSats),
7264
+ maxFeeSats: BigInt(maxFeeSats)
7265
+ });
7266
+ spentSats = spendLimitSats(result.feeMsat);
7267
+ }
7268
+ catch (err) {
7269
+ if (!(err instanceof types_6.PaymentWaitTimeoutError))
7270
+ spentSats = 0;
7271
+ throw err;
7272
+ }
7273
+ finally {
7274
+ if (!this.destroyed)
7275
+ this._refundSpend(maxFeeSats - spentSats);
7276
+ this._liveRebalanceChargeSats -= maxFeeSats;
7277
+ }
7195
7278
  this.log('info', 'Rebalance completed', {
7196
7279
  fromChannelId,
7197
7280
  toChannelId,
@@ -7211,9 +7294,37 @@ class BeignetNode extends events_1.EventEmitter {
7211
7294
  (!Number.isInteger(budgetSatsPerDay) || budgetSatsPerDay < 0)) {
7212
7295
  throw new errors_1.BeignetError(errors_1.BeignetErrorCode.INVALID_PARAMS, 'budgetSatsPerDay must be a non-negative integer');
7213
7296
  }
7214
- const summary = await this.node.executeRebalanceRecommendations({
7215
- budgetSatsPerDay
7216
- });
7297
+ this._checkDraining();
7298
+ const holdSats = this.node.rebalanceBudgetSatsPerDay(budgetSatsPerDay);
7299
+ if (!Number.isInteger(holdSats) || holdSats < 0) {
7300
+ throw new errors_1.BeignetError(errors_1.BeignetErrorCode.INVALID_PARAMS, 'autoRebalance.budgetSatsPerDay must be a non-negative integer');
7301
+ }
7302
+ this._checkSpendLimit(holdSats);
7303
+ this._recordSpend(holdSats);
7304
+ this._liveRebalanceChargeSats += holdSats;
7305
+ let spentSats = holdSats;
7306
+ let reachedPlans = false;
7307
+ let summary;
7308
+ try {
7309
+ summary = await this.node.executeRebalanceRecommendations({
7310
+ budgetSatsPerDay,
7311
+ stopRequested: () => {
7312
+ reachedPlans = true;
7313
+ return this._draining;
7314
+ }
7315
+ });
7316
+ spentSats = spendLimitSats(summary.feeSpentMsat);
7317
+ }
7318
+ catch (err) {
7319
+ if (!reachedPlans)
7320
+ spentSats = 0;
7321
+ throw err;
7322
+ }
7323
+ finally {
7324
+ if (!this.destroyed)
7325
+ this._refundSpend(holdSats - spentSats);
7326
+ this._liveRebalanceChargeSats -= holdSats;
7327
+ }
7217
7328
  return {
7218
7329
  attempts: summary.attempts.map((a) => ({
7219
7330
  fromChannelId: a.fromChannelId,
@@ -7749,12 +7860,22 @@ class BeignetNode extends events_1.EventEmitter {
7749
7860
  }
7750
7861
  async backup(destPath) {
7751
7862
  await this.storage.backup(destPath);
7863
+ await (0, backup_mac_1.writeBackupMac)((0, backup_mac_1.deriveBackupMacKey)(bip39.mnemonicToSeedSync(this.mnemonic)), destPath);
7864
+ }
7865
+ storageFiles() {
7866
+ const dbPath = fs.realpathSync.native(path.join(this.dataDir, `${this.networkName}.db`));
7867
+ return [
7868
+ dbPath,
7869
+ `${dbPath}-wal`,
7870
+ `${dbPath}-shm`,
7871
+ `${dbPath}-journal`,
7872
+ path.join(this.dataDir, `${this.networkName}.lock`)
7873
+ ];
7752
7874
  }
7753
7875
  performScheduledBackup() {
7754
7876
  if (!this.backupPath || this.destroyed)
7755
7877
  return;
7756
- this._backupPromise = this.storage
7757
- .backup(this.backupPath)
7878
+ this._backupPromise = this.backup(this.backupPath)
7758
7879
  .then(() => {
7759
7880
  this.log('info', 'Scheduled backup completed', {
7760
7881
  path: this.backupPath
@@ -8008,6 +8129,10 @@ class BeignetNode extends events_1.EventEmitter {
8008
8129
  clearInterval(this._fallbackRecoveryTimer);
8009
8130
  this._fallbackRecoveryTimer = undefined;
8010
8131
  }
8132
+ if (this._autoRebalanceTimer) {
8133
+ clearInterval(this._autoRebalanceTimer);
8134
+ this._autoRebalanceTimer = undefined;
8135
+ }
8011
8136
  this.paymentQueue?.stop();
8012
8137
  this.paymentQueue?.removeAllListeners();
8013
8138
  this.directFundingSender?.stop();
@@ -8052,6 +8177,10 @@ class BeignetNode extends events_1.EventEmitter {
8052
8177
  clearInterval(this._fallbackRecoveryTimer);
8053
8178
  this._fallbackRecoveryTimer = undefined;
8054
8179
  }
8180
+ if (this._autoRebalanceTimer) {
8181
+ clearInterval(this._autoRebalanceTimer);
8182
+ this._autoRebalanceTimer = undefined;
8183
+ }
8055
8184
  if (this._confirmTimer) {
8056
8185
  clearTimeout(this._confirmTimer);
8057
8186
  this._confirmTimer = undefined;