beignet 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +306 -1
- package/dist/cli/auth.js +238 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/beignet-node.js +1034 -57
- package/dist/cli/beignet-node.js.map +1 -1
- package/dist/cli/cli.js +1257 -18
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/config.js +45 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/daemon.js +398 -38
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/errors.js +2 -0
- package/dist/cli/errors.js.map +1 -1
- package/dist/cli/index.js +6 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/openapi.js +1343 -89
- package/dist/cli/openapi.js.map +1 -1
- package/dist/cli/restore.js +89 -0
- package/dist/cli/restore.js.map +1 -0
- package/dist/cli/wallet-storage.js +34 -0
- package/dist/cli/wallet-storage.js.map +1 -0
- package/dist/electrum/index.js +107 -12
- package/dist/electrum/index.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lightning/advisor/fee-tuner.js +42 -0
- package/dist/lightning/advisor/fee-tuner.js.map +1 -0
- package/dist/lightning/advisor/index.js +9 -1
- package/dist/lightning/advisor/index.js.map +1 -1
- package/dist/lightning/advisor/rebalance-planner.js +55 -0
- package/dist/lightning/advisor/rebalance-planner.js.map +1 -0
- package/dist/lightning/backup/index.js +18 -0
- package/dist/lightning/backup/index.js.map +1 -0
- package/dist/lightning/backup/scb.js +42 -0
- package/dist/lightning/backup/scb.js.map +1 -0
- package/dist/lightning/chain/chain-monitor.js +27 -0
- package/dist/lightning/chain/chain-monitor.js.map +1 -1
- package/dist/lightning/chain/output-resolver.js +66 -15
- package/dist/lightning/chain/output-resolver.js.map +1 -1
- package/dist/lightning/chain/types.js +1 -0
- package/dist/lightning/chain/types.js.map +1 -1
- package/dist/lightning/channel/channel-manager.js +91 -24
- package/dist/lightning/channel/channel-manager.js.map +1 -1
- package/dist/lightning/channel/channel-state.js.map +1 -1
- package/dist/lightning/channel/channel.js +122 -4
- package/dist/lightning/channel/channel.js.map +1 -1
- package/dist/lightning/channel/commitment-builder.js +4 -7
- package/dist/lightning/channel/commitment-builder.js.map +1 -1
- package/dist/lightning/channel/dual-funding.js +6 -5
- package/dist/lightning/channel/dual-funding.js.map +1 -1
- package/dist/lightning/channel/splice-tx.js.map +1 -1
- package/dist/lightning/channel/types.js +3 -1
- package/dist/lightning/channel/types.js.map +1 -1
- package/dist/lightning/channel/validation.js +3 -3
- package/dist/lightning/channel/validation.js.map +1 -1
- package/dist/lightning/crypto/index.js +1 -0
- package/dist/lightning/crypto/index.js.map +1 -1
- package/dist/lightning/crypto/message-signing.js +116 -0
- package/dist/lightning/crypto/message-signing.js.map +1 -0
- package/dist/lightning/crypto/xchacha20poly1305.js +77 -0
- package/dist/lightning/crypto/xchacha20poly1305.js.map +1 -0
- package/dist/lightning/features/flags.js +1 -0
- package/dist/lightning/features/flags.js.map +1 -1
- package/dist/lightning/gossip/messages.js +37 -1
- package/dist/lightning/gossip/messages.js.map +1 -1
- package/dist/lightning/index.js +2 -1
- package/dist/lightning/index.js.map +1 -1
- package/dist/lightning/keys/signer.js +24 -3
- package/dist/lightning/keys/signer.js.map +1 -1
- package/dist/lightning/keys/wallet-keys.js +1 -0
- package/dist/lightning/keys/wallet-keys.js.map +1 -1
- package/dist/lightning/message/index.js +1 -0
- package/dist/lightning/message/index.js.map +1 -1
- package/dist/lightning/message/peer-storage.js +43 -0
- package/dist/lightning/message/peer-storage.js.map +1 -0
- package/dist/lightning/message/types.js +2 -0
- package/dist/lightning/message/types.js.map +1 -1
- package/dist/lightning/node/lightning-node.js +1593 -236
- package/dist/lightning/node/lightning-node.js.map +1 -1
- package/dist/lightning/node/types.js +18 -1
- package/dist/lightning/node/types.js.map +1 -1
- package/dist/lightning/offer/offer-manager.js +2 -5
- package/dist/lightning/offer/offer-manager.js.map +1 -1
- package/dist/lightning/offer/tlv.js +3 -1
- package/dist/lightning/offer/tlv.js.map +1 -1
- package/dist/lightning/storage/encryption.js +67 -0
- package/dist/lightning/storage/encryption.js.map +1 -0
- package/dist/lightning/storage/index.js +1 -0
- package/dist/lightning/storage/index.js.map +1 -1
- package/dist/lightning/storage/serialization.js +6 -2
- package/dist/lightning/storage/serialization.js.map +1 -1
- package/dist/lightning/storage/sqlite-storage.js +405 -33
- package/dist/lightning/storage/sqlite-storage.js.map +1 -1
- package/dist/lightning/watchtower/blob.js +221 -0
- package/dist/lightning/watchtower/blob.js.map +1 -0
- package/dist/lightning/watchtower/index.js +55 -0
- package/dist/lightning/watchtower/index.js.map +1 -0
- package/dist/lightning/watchtower/justice.js +272 -0
- package/dist/lightning/watchtower/justice.js.map +1 -0
- package/dist/lightning/watchtower/tower-connection.js +208 -0
- package/dist/lightning/watchtower/tower-connection.js.map +1 -0
- package/dist/lightning/watchtower/types.js +3 -0
- package/dist/lightning/watchtower/types.js.map +1 -0
- package/dist/lightning/watchtower/watchtower-client.js +472 -0
- package/dist/lightning/watchtower/watchtower-client.js.map +1 -0
- package/dist/lightning/watchtower/wtwire.js +201 -0
- package/dist/lightning/watchtower/wtwire.js.map +1 -0
- package/dist/logger.js +41 -0
- package/dist/logger.js.map +1 -0
- package/dist/shapes/electrum.js +32 -1
- package/dist/shapes/electrum.js.map +1 -1
- package/dist/shapes/wallet.js +11 -1
- package/dist/shapes/wallet.js.map +1 -1
- package/dist/transaction/index.js +163 -15
- package/dist/transaction/index.js.map +1 -1
- package/dist/types/cli/auth.d.ts +34 -0
- package/dist/types/cli/beignet-node.d.ts +148 -4
- package/dist/types/cli/daemon.d.ts +5 -0
- package/dist/types/cli/errors.d.ts +2 -0
- package/dist/types/cli/index.d.ts +1 -0
- package/dist/types/cli/restore.d.ts +10 -0
- package/dist/types/cli/types.d.ts +235 -1
- package/dist/types/cli/wallet-storage.d.ts +3 -0
- package/dist/types/electrum/index.d.ts +12 -0
- package/dist/types/electrum.js +1 -0
- package/dist/types/electrum.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/lightning/advisor/fee-tuner.d.ts +20 -0
- package/dist/types/lightning/advisor/index.d.ts +4 -0
- package/dist/types/lightning/advisor/rebalance-planner.d.ts +12 -0
- package/dist/types/lightning/backup/index.d.ts +1 -0
- package/dist/types/lightning/backup/scb.d.ts +24 -0
- package/dist/types/lightning/chain/chain-monitor.d.ts +1 -0
- package/dist/types/lightning/chain/types.d.ts +1 -0
- package/dist/types/lightning/channel/channel-manager.d.ts +11 -0
- package/dist/types/lightning/channel/channel-state.d.ts +2 -0
- package/dist/types/lightning/channel/channel.d.ts +11 -5
- package/dist/types/lightning/channel/commitment-builder.d.ts +7 -7
- package/dist/types/lightning/channel/dual-funding.d.ts +2 -1
- package/dist/types/lightning/channel/splice-tx.d.ts +2 -2
- package/dist/types/lightning/channel/types.d.ts +2 -0
- package/dist/types/lightning/channel/validation.d.ts +1 -1
- package/dist/types/lightning/crypto/index.d.ts +1 -0
- package/dist/types/lightning/crypto/message-signing.d.ts +9 -0
- package/dist/types/lightning/crypto/xchacha20poly1305.d.ts +3 -0
- package/dist/types/lightning/features/flags.d.ts +1 -0
- package/dist/types/lightning/gossip/messages.d.ts +4 -0
- package/dist/types/lightning/index.d.ts +1 -0
- package/dist/types/lightning/keys/signer.d.ts +18 -1
- package/dist/types/lightning/keys/wallet-keys.d.ts +2 -1
- package/dist/types/lightning/message/index.d.ts +1 -0
- package/dist/types/lightning/message/peer-storage.d.ts +9 -0
- package/dist/types/lightning/message/types.d.ts +2 -0
- package/dist/types/lightning/node/lightning-node.d.ts +120 -4
- package/dist/types/lightning/node/types.d.ts +66 -1
- package/dist/types/lightning/storage/encryption.d.ts +11 -0
- package/dist/types/lightning/storage/index.d.ts +1 -0
- package/dist/types/lightning/storage/serialization.d.ts +2 -0
- package/dist/types/lightning/storage/sqlite-storage.d.ts +43 -3
- package/dist/types/lightning/storage/types.d.ts +62 -0
- package/dist/types/lightning/watchtower/blob.d.ts +46 -0
- package/dist/types/lightning/watchtower/index.d.ts +9 -0
- package/dist/types/lightning/watchtower/justice.d.ts +30 -0
- package/dist/types/lightning/watchtower/tower-connection.d.ts +34 -0
- package/dist/types/lightning/watchtower/types.d.ts +46 -0
- package/dist/types/lightning/watchtower/watchtower-client.d.ts +68 -0
- package/dist/types/lightning/watchtower/wtwire.d.ts +90 -0
- package/dist/types/logger.d.ts +17 -0
- package/dist/types/shapes/electrum.d.ts +3 -1
- package/dist/types/transaction/index.d.ts +6 -0
- package/dist/types/transaction.js.map +1 -1
- package/dist/types/types/electrum.d.ts +3 -2
- package/dist/types/types/transaction.d.ts +1 -1
- package/dist/types/types/wallet.d.ts +80 -4
- package/dist/types/utils/descriptors.d.ts +2 -0
- package/dist/types/utils/fees.d.ts +3 -0
- package/dist/types/utils/helpers.d.ts +3 -1
- package/dist/types/utils/index.d.ts +5 -0
- package/dist/types/utils/multisig.d.ts +29 -0
- package/dist/types/utils/transaction.d.ts +7 -0
- package/dist/types/utils/wallet-storage-encryption.d.ts +3 -0
- package/dist/types/utils/wallet.d.ts +1 -1
- package/dist/types/utils/watch-only.d.ts +12 -0
- package/dist/types/wallet/constants.d.ts +1 -0
- package/dist/types/wallet/index.d.ts +63 -4
- package/dist/types/wallet.js +3 -0
- package/dist/types/wallet.js.map +1 -1
- package/dist/utils/derivation-path.js +8 -7
- package/dist/utils/derivation-path.js.map +1 -1
- package/dist/utils/descriptors.js +56 -0
- package/dist/utils/descriptors.js.map +1 -0
- package/dist/utils/electrum.js +14 -4
- package/dist/utils/electrum.js.map +1 -1
- package/dist/utils/fees.js +19 -0
- package/dist/utils/fees.js.map +1 -0
- package/dist/utils/helpers.js +22 -5
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/multisig.js +90 -0
- package/dist/utils/multisig.js.map +1 -0
- package/dist/utils/transaction.js +30 -3
- package/dist/utils/transaction.js.map +1 -1
- package/dist/utils/wallet-storage-encryption.js +45 -0
- package/dist/utils/wallet-storage-encryption.js.map +1 -0
- package/dist/utils/wallet.js +3 -1
- package/dist/utils/wallet.js.map +1 -1
- package/dist/utils/watch-only.js +102 -0
- package/dist/utils/watch-only.js.map +1 -0
- package/dist/wallet/constants.js +2 -1
- package/dist/wallet/constants.js.map +1 -1
- package/dist/wallet/index.js +810 -51
- package/dist/wallet/index.js.map +1 -1
- package/docs/ROADMAP.md +365 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Beignet provides two layers of Bitcoin wallet functionality:
|
|
|
13
13
|
|
|
14
14
|
1. [Getting Started](#getting-started)
|
|
15
15
|
2. [On-Chain Wallet](#on-chain-wallet)
|
|
16
|
+
- [Leveled Logging](#leveled-logging)
|
|
16
17
|
3. [Lightning Network](#lightning-network)
|
|
17
18
|
- [Lightning Quick Start (BeignetNode)](#lightning-quick-start-beignetnode)
|
|
18
19
|
- [Decision-Support APIs](#decision-support-apis)
|
|
@@ -137,6 +138,231 @@ const utxos = wallet.value.listUtxos();
|
|
|
137
138
|
const history = await wallet.value.getAddressHistory('bc1q...');
|
|
138
139
|
```
|
|
139
140
|
|
|
141
|
+
### Watch-Only Wallets
|
|
142
|
+
|
|
143
|
+
A watch-only wallet is constructed from an account-level extended public key
|
|
144
|
+
(xpub/ypub/zpub for mainnet, tpub/upub/vpub for testnet/regtest) instead of a
|
|
145
|
+
mnemonic. The key is assumed to sit at the account level
|
|
146
|
+
(m/purpose'/coin'/account', e.g. m/84'/0'/0' for p2wpkh), so receive and
|
|
147
|
+
change addresses derive publicly as xpub/0/i and xpub/1/i. SLIP-132 version
|
|
148
|
+
bytes are normalized automatically: a zpub/vpub implies p2wpkh and a
|
|
149
|
+
ypub/upub implies p2sh-p2wpkh; a plain xpub/tpub uses the `addressType`
|
|
150
|
+
option (default p2wpkh). Because one account xpub yields exactly one address
|
|
151
|
+
type, a watch-only wallet monitors only that type.
|
|
152
|
+
|
|
153
|
+
Everything read-only works: address generation, gap-limit scanning, Electrum
|
|
154
|
+
refresh, balances, transaction history, UTXOs, fee estimates and address
|
|
155
|
+
subscriptions. Anything that requires private keys
|
|
156
|
+
(send/sendMax/sendMany/sweepPrivateKey/getPrivateKey and the internal signing
|
|
157
|
+
paths) fails with the typed `WatchOnlySigningError`
|
|
158
|
+
(`code: 'WATCH_ONLY_CANNOT_SIGN'`, message `watch-only wallet cannot sign`).
|
|
159
|
+
|
|
160
|
+
Watch-only is a library feature only for now: the HTTP daemon always runs
|
|
161
|
+
with a mnemonic.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { Wallet } from 'beignet';
|
|
165
|
+
|
|
166
|
+
const res = await Wallet.createWatchOnly({
|
|
167
|
+
xpub: 'zpub6r...',
|
|
168
|
+
network: 'bitcoin',
|
|
169
|
+
electrumOptions: { net, tls },
|
|
170
|
+
});
|
|
171
|
+
if (res.isErr()) return;
|
|
172
|
+
const watchOnly = res.value;
|
|
173
|
+
|
|
174
|
+
const address = await watchOnly.getAddress(); // works
|
|
175
|
+
const balance = watchOnly.getBalance(); // works
|
|
176
|
+
const sendRes = await watchOnly.send({ address: 'bc1q...', amount: 1000 });
|
|
177
|
+
// sendRes.isErr() === true; sendRes.error.message === 'watch-only wallet cannot sign'
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### External Signer (Hardware Wallet) PSBT Flow
|
|
181
|
+
|
|
182
|
+
`buildPsbt` runs the normal transaction setup (coin selection, change, fee)
|
|
183
|
+
but stops before signing and returns a base64 PSBT populated with everything
|
|
184
|
+
a hardware signer needs: `witnessUtxo` (or `nonWitnessUtxo` for legacy
|
|
185
|
+
p2pkh), `redeemScript` for p2sh-p2wpkh, `tapInternalKey` plus
|
|
186
|
+
`tapBip32Derivation` for p2tr, and `bip32Derivation` (fingerprint + path +
|
|
187
|
+
pubkey) on every wallet input. It works on both full and watch-only wallets.
|
|
188
|
+
Note for watch-only wallets: the true master fingerprint is unknowable from
|
|
189
|
+
an account xpub, so the xpub's parent fingerprint is used; signers should
|
|
190
|
+
locate keys by derivation path.
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// 1. Build (works on a watch-only wallet)
|
|
194
|
+
const build = await wallet.buildPsbt({
|
|
195
|
+
address: 'bc1q...',
|
|
196
|
+
amount: 50000,
|
|
197
|
+
satsPerByte: 4,
|
|
198
|
+
});
|
|
199
|
+
if (build.isErr()) return;
|
|
200
|
+
const { psbtBase64, fee, vsizeEstimate } = build.value;
|
|
201
|
+
|
|
202
|
+
// 2. Sign externally (hardware wallet / HWI / another machine)
|
|
203
|
+
const signedBase64 = await myHardwareWallet.signPsbt(psbtBase64);
|
|
204
|
+
|
|
205
|
+
// 3. Import: validates a signature on EVERY input, finalizes, does NOT broadcast
|
|
206
|
+
const imported = wallet.importSignedPsbt(signedBase64);
|
|
207
|
+
if (imported.isErr()) return; // missing/invalid signatures are rejected loudly
|
|
208
|
+
const { txHex, txid } = imported.value;
|
|
209
|
+
|
|
210
|
+
// 4. Broadcast when ready
|
|
211
|
+
await wallet.broadcastTransaction(txHex);
|
|
212
|
+
|
|
213
|
+
// Multi-party signing: merge partially signed copies of the same PSBT
|
|
214
|
+
const combined = wallet.combinePsbts([copyA, copyB]);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The HTTP daemon exposes the same flow on its (mnemonic-backed) wallet via
|
|
218
|
+
`POST /psbt/build`, `POST /psbt/import-signed` and `POST /psbt/combine`, and
|
|
219
|
+
the CLI via `beignet psbt build|import-signed|combine`.
|
|
220
|
+
|
|
221
|
+
### Multisig P2WSH Wallets (sortedmulti)
|
|
222
|
+
|
|
223
|
+
`Wallet.createMultisig` creates a descriptor-based sorted-multisig wallet:
|
|
224
|
+
`wsh(sortedmulti(threshold, key1, key2, ...))`, the interoperable standard
|
|
225
|
+
used by Bitcoin Core, Sparrow and Specter. Derivation follows BIP 48 with
|
|
226
|
+
script type 2 (`m/48'/coin'/account'/2'`, receive `/0/*`, change `/1/*`) and
|
|
227
|
+
public keys are ordered per BIP 67 at every index, so any wallet built from
|
|
228
|
+
the same account xpubs produces identical addresses regardless of the order
|
|
229
|
+
the cosigners were listed in.
|
|
230
|
+
|
|
231
|
+
Cosigners are supplied as account-level extended public keys (`xpub`/`tpub`,
|
|
232
|
+
or the SLIP-132 multisig encodings `Zpub`/`Vpub`, normalized automatically).
|
|
233
|
+
When a mnemonic is provided, this wallet IS one of the cosigners: its BIP 48
|
|
234
|
+
account xpub is derived and included automatically (pass `ourXpub` to assert
|
|
235
|
+
it explicitly; a mismatch is rejected). Omit the mnemonic for a watch-only
|
|
236
|
+
multisig coordinator: the full read-only surface (scanning, balances,
|
|
237
|
+
history, subscriptions) works, signing does not.
|
|
238
|
+
|
|
239
|
+
Spending is PSBT-only. Direct spends (`send`/`sendMany`/`sendMax`) fail with
|
|
240
|
+
the typed `MultisigSpendError` (`code: 'MULTISIG_REQUIRES_PSBT'`). `buildPsbt`
|
|
241
|
+
attaches the `witnessScript` and one `bip32Derivation` entry per cosigner to
|
|
242
|
+
every input; `signPsbtWithOurKey` adds this cosigner's partial signature
|
|
243
|
+
without finalizing; `importSignedPsbt` counts the VALID partial signatures on
|
|
244
|
+
each input against the witnessScript threshold and refuses to finalize below
|
|
245
|
+
it (the error names how many signatures it has and needs).
|
|
246
|
+
`exportDescriptors()` emits the checksummed `wsh(sortedmulti(...))` receive
|
|
247
|
+
and change descriptors for import into Bitcoin Core/Sparrow/Specter; our key
|
|
248
|
+
carries its full key origin, cosigners known only as xpubs carry a
|
|
249
|
+
fingerprint-only origin. Multisig is a library-only feature for now: the
|
|
250
|
+
HTTP daemon wallet stays single-sig.
|
|
251
|
+
|
|
252
|
+
Full 2-of-3 walkthrough:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { Wallet } from 'beignet';
|
|
256
|
+
|
|
257
|
+
// Each cosigner shares their BIP 48 account xpub (m/48'/0'/0'/2' on mainnet).
|
|
258
|
+
// Grab ours from exportDescriptors() or derive it with any BIP 48 tool.
|
|
259
|
+
|
|
260
|
+
// 1. Cosigner A creates their multisig wallet (A holds mnemonicA).
|
|
261
|
+
const walletA = (
|
|
262
|
+
await Wallet.createMultisig({
|
|
263
|
+
threshold: 2,
|
|
264
|
+
mnemonic: mnemonicA, // we are one cosigner; our xpub is added automatically
|
|
265
|
+
cosigners: [xpubB, xpubC],
|
|
266
|
+
network: 'bitcoin',
|
|
267
|
+
electrumOptions: { net, tls },
|
|
268
|
+
})
|
|
269
|
+
).value;
|
|
270
|
+
|
|
271
|
+
// Cosigner B does the same in their own instance/machine.
|
|
272
|
+
const walletB = (
|
|
273
|
+
await Wallet.createMultisig({
|
|
274
|
+
threshold: 2,
|
|
275
|
+
mnemonic: mnemonicB,
|
|
276
|
+
cosigners: [xpubA, xpubC],
|
|
277
|
+
network: 'bitcoin',
|
|
278
|
+
electrumOptions: { net, tls },
|
|
279
|
+
})
|
|
280
|
+
).value;
|
|
281
|
+
|
|
282
|
+
// An optional watch-only coordinator holds no keys at all.
|
|
283
|
+
const coordinator = (
|
|
284
|
+
await Wallet.createMultisig({
|
|
285
|
+
threshold: 2,
|
|
286
|
+
cosigners: [xpubA, xpubB, xpubC],
|
|
287
|
+
network: 'bitcoin',
|
|
288
|
+
electrumOptions: { net, tls },
|
|
289
|
+
})
|
|
290
|
+
).value;
|
|
291
|
+
|
|
292
|
+
// 2. Fund the multisig: every instance derives the same addresses.
|
|
293
|
+
const deposit = await walletA.getAddress(); // == walletB/coordinator address
|
|
294
|
+
|
|
295
|
+
// 3. Build the unsigned PSBT (works on any instance, coordinator included).
|
|
296
|
+
const built = await walletA.buildPsbt({
|
|
297
|
+
address: 'bc1q...',
|
|
298
|
+
amount: 50000,
|
|
299
|
+
satsPerByte: 4,
|
|
300
|
+
});
|
|
301
|
+
const unsigned = built.value.psbtBase64;
|
|
302
|
+
|
|
303
|
+
// 4. Each cosigner signs their own copy (below threshold nothing finalizes).
|
|
304
|
+
const signedA = walletA.signPsbtWithOurKey(unsigned).value;
|
|
305
|
+
const signedB = walletB.signPsbtWithOurKey(unsigned).value;
|
|
306
|
+
|
|
307
|
+
// 5. Combine the partials, finalize at threshold, broadcast.
|
|
308
|
+
const combined = coordinator.combinePsbts([signedA, signedB]).value;
|
|
309
|
+
const finalized = coordinator.importSignedPsbt(combined).value; // 2-of-3 met
|
|
310
|
+
await coordinator.broadcastTransaction(finalized.txHex);
|
|
311
|
+
|
|
312
|
+
// Importing with only one signature fails loudly:
|
|
313
|
+
// 'Input 0 is below the multisig threshold: have 1 signature(s), need 2.'
|
|
314
|
+
|
|
315
|
+
// Interop: import the wallet into Bitcoin Core/Sparrow/Specter.
|
|
316
|
+
const descriptors = walletA.exportDescriptors().value;
|
|
317
|
+
// wsh(sortedmulti(2,[fp/48h/0h/0h/2h]xpub.../0/*,[fp]xpub.../0/*,...))#checksum
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Networks, Fee Estimates & Electrum Failover
|
|
321
|
+
|
|
322
|
+
- **Networks:** `mainnet`, `testnet`, `regtest`, and `signet` are supported end to end (on-chain wallet, Electrum, CLI/daemon via `--network signet`, and the Lightning node config, which uses the signet chain hash and `tbs` invoice prefix). Signet shares testnet's address formats and derivation paths (coin type 1); only the chain differs.
|
|
323
|
+
- **Fee estimation source:** `Wallet.create({ feeEstimationSource })` accepts `'electrum' | 'http' | 'auto'` (default `'auto'`). `'electrum'` queries only the connected Electrum server via `blockchain.estimatefee`, so fee lookups never leak to mempool.space/blocktank over clearnet; `'auto'` prefers Electrum and falls back to HTTP only when Electrum is unavailable or returns unusable values. All remote-sourced rates are clamped to at most 5000 sat/vB. The daemon exposes the same option as `feeEstimationSource` / `--fee-source` / `BEIGNET_FEE_SOURCE`.
|
|
324
|
+
- **Electrum failover:** when multiple `electrumOptions.servers` are provided, the wallet rotates through them in order on connect/reconnect failure (then through hardcoded fallback peers for the network), with a per-server cooldown so dead servers are not hammered. `wallet.electrum.currentServer` and `wallet.electrum.rotationCount` expose the current server and rotation history.
|
|
325
|
+
- **BIP21:** `encodeBip21({ address, amountSats?, label?, message? })` builds a `bitcoin:` payment URI; the daemon's `POST /address/new` accepts `{ bip21: true, amountSats?, label?, message? }` and the CLI supports `address --bip21 [--amount <sats>] [--label L] [--message M]`.
|
|
326
|
+
|
|
327
|
+
### Storage & Encryption
|
|
328
|
+
|
|
329
|
+
The wallet persists its state through the host-injected `TStorage` interface (`storage: { getData, setData }` on `Wallet.create`). Values are handed to the host as-is, so by default they are stored in plaintext. The persisted data is addresses, address indexes, UTXOs, transactions, balance and fee estimates. No private keys and no mnemonic are ever written, so exposure is a privacy concern (full wallet history), not fund loss.
|
|
330
|
+
|
|
331
|
+
To encrypt at rest, wrap any `TStorage` with `createEncryptedStorage` before passing it in. Values are encrypted with AES-256-GCM under a key derived from the seed via HKDF, and pre-existing plaintext values are passed through unchanged and migrate lazily as they are rewritten:
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
import { createEncryptedStorage, Wallet } from 'beignet';
|
|
335
|
+
import * as bip39 from 'bip39';
|
|
336
|
+
|
|
337
|
+
const seed = bip39.mnemonicToSeedSync(mnemonic);
|
|
338
|
+
const wallet = await Wallet.create({
|
|
339
|
+
mnemonic,
|
|
340
|
+
storage: createEncryptedStorage({ getData, setData }, seed),
|
|
341
|
+
// ...
|
|
342
|
+
});
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Leveled Logging
|
|
346
|
+
|
|
347
|
+
Diagnostic output (debug/info/warn/error) flows through a small injectable logger, kept separate from the Lightning node's persisted structured action log (`getActionLog`). The `ILogger` interface is four methods, `debug`/`info`/`warn`/`error`(`message: string, meta?: unknown`), with level filtering `debug < info < warn < error` plus `'silent'`:
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { Wallet, createConsoleLogger, noopLogger } from 'beignet';
|
|
351
|
+
|
|
352
|
+
const wallet = await Wallet.create({
|
|
353
|
+
mnemonic,
|
|
354
|
+
logger: createConsoleLogger('warn'), // only warn + error reach the console
|
|
355
|
+
// logger: noopLogger, // fully silent
|
|
356
|
+
// logger: myLogger, // any ILogger: route into your own stack
|
|
357
|
+
// ...
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
- **`Wallet.create({ logger })`** defaults to `createConsoleLogger('info')`, which preserves the wallet's historical console output. `disableMessages` is independent and keeps its existing meaning: it only gates `onMessage` callbacks.
|
|
362
|
+
- **`LightningNode`** accepts `logger` in `INodeConfig` / `fromMnemonic` options and defaults to `noopLogger` (the node prints nothing, as before). Every structured action-log entry is additionally mirrored to `logger.debug('category:action', data)`.
|
|
363
|
+
- **`BeignetNode.create({ logger, logLevel })`**: log entries that pass `logLevel` are forwarded to the injected logger (in addition to the `'log'` event), and the logger is injected into the underlying `Wallet` and `LightningNode`. Without `logger`, behavior is unchanged (events only).
|
|
364
|
+
- **Daemon:** `beignet start --log-level <debug|info|warn|error|silent>` (or `BEIGNET_LOG_LEVEL`, or `logLevel` in `~/.beignet/config.json`) prints leveled diagnostics to stderr. Unset keeps the daemon silent (the default); stdout stays reserved for command output.
|
|
365
|
+
|
|
140
366
|
## Lightning Network
|
|
141
367
|
|
|
142
368
|
### Lightning Quick Start (BeignetNode)
|
|
@@ -212,6 +438,85 @@ const readiness = node.getMainnetReadiness();
|
|
|
212
438
|
console.log('Score:', readiness.score + '/100', 'Ready:', readiness.ready);
|
|
213
439
|
```
|
|
214
440
|
|
|
441
|
+
### Advisor Execution (circular rebalancing + fee auto-tuning)
|
|
442
|
+
|
|
443
|
+
The advisor can also *act*, not just recommend. Both features are **off by
|
|
444
|
+
default** and only run when explicitly enabled in the node options.
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
// One-shot circular rebalance: self-payment out over fromChannelId and back
|
|
448
|
+
// in over toChannelId. Aborts WITHOUT paying if the route fee > maxFeeSats.
|
|
449
|
+
const result = await node.rebalanceChannel(
|
|
450
|
+
fromChannelId, toChannelId, 50_000, /* maxFeeSats: */ 50
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
// Inspect what the executor would do (read-only)
|
|
454
|
+
const recs = node.getAdvisorRecommendations(); // analyze() + rebalancePlan[]
|
|
455
|
+
|
|
456
|
+
// Run the advisor's rebalance plan under a per-day fee budget
|
|
457
|
+
const summary = await node.executeRebalances(/* budgetSatsPerDay: */ 500);
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Automatic modes (opt-in via `BeignetNodeOptions` / `INodeConfig`):
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
const node = await BeignetNode.create({
|
|
464
|
+
mnemonic,
|
|
465
|
+
// Periodically executes the rebalance plan. Routing fees spent on
|
|
466
|
+
// rebalances are capped at budgetSatsPerDay per UTC day; the running spend
|
|
467
|
+
// is persisted, so restarts never overspend the same day. The budget
|
|
468
|
+
// resets at midnight UTC.
|
|
469
|
+
autoRebalance: { enabled: true, budgetSatsPerDay: 500, minImbalancePct: 20 },
|
|
470
|
+
// Every intervalMs (default 6h) nudges each channel's proportional fee:
|
|
471
|
+
// +25% when outbound is depleted (<20% local) but still forwarding,
|
|
472
|
+
// -25% when the channel saw no forwards in the window, clamped to
|
|
473
|
+
// [floorPpm, ceilPpm]. One adjustment per channel per interval.
|
|
474
|
+
autoTuneFees: { enabled: true, floorPpm: 1, ceilPpm: 5_000 }
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Daemon/CLI surfaces: `POST /rebalance`, `GET /advisor/recommendations`,
|
|
479
|
+
`POST /advisor/execute-rebalances`; `beignet rebalance <from> <to> <sats>
|
|
480
|
+
--max-fee <sats>`, `beignet advisor recommendations`, `beignet advisor
|
|
481
|
+
execute-rebalances [--budget <sats>]`.
|
|
482
|
+
|
|
483
|
+
### Watchtowers
|
|
484
|
+
|
|
485
|
+
Penalty enforcement normally requires this node's own chain monitor to be online:
|
|
486
|
+
if a counterparty broadcasts a revoked commitment while you are offline, nobody
|
|
487
|
+
sweeps the breach. The **watchtower client** closes that gap. At every revocation
|
|
488
|
+
it builds an encrypted *justice kit* (the revoked commitment's breach hint plus a
|
|
489
|
+
pre-signed to_local penalty) and ships it to one or more remote towers over the
|
|
490
|
+
standard BOLT 8 Noise transport. When a tower later sees the breach transaction on
|
|
491
|
+
chain, it decrypts the kit and broadcasts the penalty on your behalf — reclaiming
|
|
492
|
+
the channel even though you never came back online.
|
|
493
|
+
|
|
494
|
+
- **Altruist only.** Sessions use `reward = 0`; towers take no cut. There is no
|
|
495
|
+
server mode (beignet is a tower *client*, not a tower).
|
|
496
|
+
- **LND-tower compatible.** Implements LND's `wtwire` protocol (Init/CreateSession/
|
|
497
|
+
StateUpdate/DeleteSession, message types 600-607) and the version-0 justice blob
|
|
498
|
+
(XChaCha20-Poly1305, breach hint = `SHA256(txid)[:16]`, key = `SHA256(txid‖txid)`),
|
|
499
|
+
so it interoperates with existing public LND altruist towers.
|
|
500
|
+
- **Legacy + anchor channels.** The to_local revocation penalty (the fund-critical
|
|
501
|
+
breach punishment) is packed for both; taproot channels are not yet backed up.
|
|
502
|
+
- **Durable.** Per-tower session state and the un-acked update backlog are persisted
|
|
503
|
+
(encrypted at rest) and drained with exponential backoff on reconnect. An un-acked
|
|
504
|
+
update is never dropped silently.
|
|
505
|
+
|
|
506
|
+
Configure towers as `pubkey@host:port` URIs (off when empty):
|
|
507
|
+
|
|
508
|
+
```ts
|
|
509
|
+
const node = await BeignetNode.create({
|
|
510
|
+
mnemonic,
|
|
511
|
+
watchtowers: ['03abc...@tower.example.com:9911']
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Daemon/CLI surfaces: `GET /watchtowers`, `POST /watchtower/add`,
|
|
516
|
+
`DELETE /watchtower/remove`; `beignet watchtower list`, `beignet watchtower add
|
|
517
|
+
<pubkey@host:port>`, `beignet watchtower remove <uri>`; daemon flag `--watchtower`
|
|
518
|
+
(repeatable) or `BEIGNET_WATCHTOWERS` (comma-separated).
|
|
519
|
+
|
|
215
520
|
### HTTP Daemon
|
|
216
521
|
|
|
217
522
|
BeignetNode can also run as an HTTP/SSE daemon for language-agnostic integrations:
|
|
@@ -512,7 +817,7 @@ Beignet is under active development. The following features are missing or carry
|
|
|
512
817
|
|
|
513
818
|
| Feature | Status | Impact |
|
|
514
819
|
|---------|--------|--------|
|
|
515
|
-
| **Watchtowers** |
|
|
820
|
+
| **Watchtowers** | Client implemented (altruist) | Ships encrypted justice data to remote LND altruist towers at every revocation so a breach is punished while you are offline (see [Watchtowers](#watchtowers)). Legacy + anchor channels only; taproot channels are not yet backed up, and server mode is out of scope. |
|
|
516
821
|
| **LSP / LSPS protocols** | Not implemented | No automated inbound liquidity acquisition via LSPS0/1/2. Liquidity ads (bLIP-51) are supported for negotiated leases; otherwise open channels manually. |
|
|
517
822
|
| **Trampoline routing** | Not implemented | All route computation is local. Cannot delegate pathfinding to a trampoline node. |
|
|
518
823
|
| **BOLT 12 offers** | Newer | Offer creation/decoding, invoice_request/invoice over onion messages, and receive-side settlement are implemented, but the surface is newer and less battle-tested than BOLT 11. Prefer BOLT 11 invoices for production. |
|
package/dist/cli/auth.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ApiKeyAuthenticator = exports.scopesAllowRoute = exports.getRouteScopes = exports.ROUTE_SCOPES = exports.API_SCOPES = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
exports.API_SCOPES = new Set([
|
|
7
|
+
'readonly',
|
|
8
|
+
'invoice',
|
|
9
|
+
'admin'
|
|
10
|
+
]);
|
|
11
|
+
exports.ROUTE_SCOPES = {
|
|
12
|
+
'GET /info': ['readonly'],
|
|
13
|
+
'GET /balance': ['readonly'],
|
|
14
|
+
'GET /peers': ['readonly'],
|
|
15
|
+
'GET /channels': ['readonly'],
|
|
16
|
+
'GET /channels/ready': ['readonly'],
|
|
17
|
+
'GET /payments': ['readonly'],
|
|
18
|
+
'GET /payment': ['readonly'],
|
|
19
|
+
'GET /payment/proof': ['readonly'],
|
|
20
|
+
'GET /payment/verify-proof': ['readonly'],
|
|
21
|
+
'GET /forwards': ['readonly'],
|
|
22
|
+
'GET /forwards/summary': ['readonly'],
|
|
23
|
+
'GET /readiness': ['readonly'],
|
|
24
|
+
'GET /stats': ['readonly'],
|
|
25
|
+
'GET /spend-limit': ['readonly'],
|
|
26
|
+
'GET /liquidity': ['readonly'],
|
|
27
|
+
'GET /watchtowers': ['readonly'],
|
|
28
|
+
'GET /advisor/recommendations': ['readonly'],
|
|
29
|
+
'GET /fees': ['readonly'],
|
|
30
|
+
'GET /fees/estimates': ['readonly'],
|
|
31
|
+
'GET /transactions': ['readonly'],
|
|
32
|
+
'GET /transactions/boostable': ['readonly'],
|
|
33
|
+
'GET /utxos': ['readonly'],
|
|
34
|
+
'GET /address/labels': ['readonly'],
|
|
35
|
+
'GET /wallet/descriptors': ['readonly'],
|
|
36
|
+
'GET /channel': ['readonly'],
|
|
37
|
+
'GET /channel/health': ['readonly'],
|
|
38
|
+
'GET /channel/policy': ['readonly'],
|
|
39
|
+
'GET /channel/diagnostics': ['readonly'],
|
|
40
|
+
'GET /channel/suggestions': ['readonly'],
|
|
41
|
+
'GET /logs': ['readonly'],
|
|
42
|
+
'GET /node/uri': ['readonly'],
|
|
43
|
+
'GET /trusted-peers': ['readonly'],
|
|
44
|
+
'GET /can-send': ['readonly'],
|
|
45
|
+
'GET /graph/info': ['readonly'],
|
|
46
|
+
'GET /graph/node': ['readonly'],
|
|
47
|
+
'GET /graph/channel': ['readonly'],
|
|
48
|
+
'GET /graph/describe': ['readonly'],
|
|
49
|
+
'GET /backup/scb': ['readonly'],
|
|
50
|
+
'GET /backup/peer-retrieved': ['readonly'],
|
|
51
|
+
'GET /queue': ['readonly'],
|
|
52
|
+
'POST /route/estimate': ['readonly'],
|
|
53
|
+
'POST /route/query': ['readonly'],
|
|
54
|
+
'POST /payment/estimate': ['readonly'],
|
|
55
|
+
'POST /message/verify': ['readonly'],
|
|
56
|
+
'POST /address/validate': ['readonly'],
|
|
57
|
+
'POST /node/wait-ready': ['readonly'],
|
|
58
|
+
'POST /channel/wait-ready': ['readonly'],
|
|
59
|
+
'GET /invoices': ['readonly', 'invoice'],
|
|
60
|
+
'GET /invoice': ['readonly', 'invoice'],
|
|
61
|
+
'GET /invoices/held': ['readonly', 'invoice'],
|
|
62
|
+
'GET /offers': ['readonly', 'invoice'],
|
|
63
|
+
'GET /can-receive': ['readonly', 'invoice'],
|
|
64
|
+
'GET /events': ['readonly', 'invoice'],
|
|
65
|
+
'POST /invoice/decode': ['readonly', 'invoice'],
|
|
66
|
+
'POST /invoice/validate': ['readonly', 'invoice'],
|
|
67
|
+
'POST /offer/decode': ['readonly', 'invoice'],
|
|
68
|
+
'POST /payment/wait': ['readonly', 'invoice'],
|
|
69
|
+
'POST /invoice/create': ['invoice'],
|
|
70
|
+
'POST /invoice/create-hold': ['invoice'],
|
|
71
|
+
'POST /invoice/settle-hold': ['invoice'],
|
|
72
|
+
'POST /invoice/cancel-hold': ['invoice'],
|
|
73
|
+
'POST /offer/create': ['invoice'],
|
|
74
|
+
'POST /address/new': ['invoice'],
|
|
75
|
+
'GET /mnemonic': [],
|
|
76
|
+
'GET /webhooks': [],
|
|
77
|
+
'POST /send': [],
|
|
78
|
+
'POST /send-max': [],
|
|
79
|
+
'POST /tx/bump-fee': [],
|
|
80
|
+
'POST /tx/boost': [],
|
|
81
|
+
'POST /consolidate': [],
|
|
82
|
+
'POST /invoice/pay': [],
|
|
83
|
+
'POST /invoice/pay-safe': [],
|
|
84
|
+
'POST /invoice/pay-async': [],
|
|
85
|
+
'POST /invoice/pay-retry': [],
|
|
86
|
+
'POST /keysend': [],
|
|
87
|
+
'POST /keysend/safe': [],
|
|
88
|
+
'POST /offer/pay': [],
|
|
89
|
+
'POST /payment/send-to-route': [],
|
|
90
|
+
'POST /payment/cancel': [],
|
|
91
|
+
'POST /queue/add': [],
|
|
92
|
+
'POST /queue/cancel': [],
|
|
93
|
+
'POST /rebalance': [],
|
|
94
|
+
'POST /advisor/execute-rebalances': [],
|
|
95
|
+
'POST /recover-fallback-funds': [],
|
|
96
|
+
'POST /utxo/freeze': [],
|
|
97
|
+
'POST /utxo/unfreeze': [],
|
|
98
|
+
'POST /address/label': [],
|
|
99
|
+
'POST /wallet/refresh': [],
|
|
100
|
+
'POST /psbt/build': [],
|
|
101
|
+
'POST /psbt/import-signed': [],
|
|
102
|
+
'POST /psbt/combine': [],
|
|
103
|
+
'POST /peer/connect': [],
|
|
104
|
+
'POST /peer/disconnect': [],
|
|
105
|
+
'POST /peers/bootstrap': [],
|
|
106
|
+
'POST /peers/connect-seeds': [],
|
|
107
|
+
'POST /trusted-peer/add': [],
|
|
108
|
+
'POST /trusted-peer/remove': [],
|
|
109
|
+
'POST /channel/open': [],
|
|
110
|
+
'POST /channel/open-zeroconf': [],
|
|
111
|
+
'POST /channel/open-v2': [],
|
|
112
|
+
'POST /channel/open-and-wait': [],
|
|
113
|
+
'POST /channel/connect-and-open': [],
|
|
114
|
+
'POST /channels/ensure-minimum': [],
|
|
115
|
+
'POST /channel/close': [],
|
|
116
|
+
'POST /channel/forceclose': [],
|
|
117
|
+
'POST /channel/splice-in': [],
|
|
118
|
+
'POST /channel/splice-out': [],
|
|
119
|
+
'POST /channel/update-commitment-feerate': [],
|
|
120
|
+
'POST /channel/update-fee': [],
|
|
121
|
+
'POST /channel/update-policy': [],
|
|
122
|
+
'POST /message/sign': [],
|
|
123
|
+
'POST /gossip/sync': [],
|
|
124
|
+
'POST /gossip/sync-rapid': [],
|
|
125
|
+
'POST /route/probe': [],
|
|
126
|
+
'POST /payment/metadata': [],
|
|
127
|
+
'POST /backup': [],
|
|
128
|
+
'POST /backup/trigger': [],
|
|
129
|
+
'POST /restore/scb': [],
|
|
130
|
+
'POST /watchtower/add': [],
|
|
131
|
+
'DELETE /watchtower/remove': [],
|
|
132
|
+
'POST /webhooks/register': [],
|
|
133
|
+
'DELETE /webhooks/unregister': [],
|
|
134
|
+
'POST /stop': [],
|
|
135
|
+
'GET /auth/keys': [],
|
|
136
|
+
'POST /auth/keys/revoke': []
|
|
137
|
+
};
|
|
138
|
+
function getRouteScopes(routeKey) {
|
|
139
|
+
return exports.ROUTE_SCOPES[routeKey] ?? [];
|
|
140
|
+
}
|
|
141
|
+
exports.getRouteScopes = getRouteScopes;
|
|
142
|
+
function scopesAllowRoute(scopes, routeKey) {
|
|
143
|
+
if (scopes.has('admin'))
|
|
144
|
+
return true;
|
|
145
|
+
return getRouteScopes(routeKey).some((s) => scopes.has(s));
|
|
146
|
+
}
|
|
147
|
+
exports.scopesAllowRoute = scopesAllowRoute;
|
|
148
|
+
function sha256(input) {
|
|
149
|
+
return (0, crypto_1.createHash)('sha256').update(input, 'utf8').digest();
|
|
150
|
+
}
|
|
151
|
+
class ApiKeyAuthenticator {
|
|
152
|
+
constructor(apiToken, apiKeys) {
|
|
153
|
+
this.candidates = [];
|
|
154
|
+
this.revoked = new Set();
|
|
155
|
+
this.keyNames = [];
|
|
156
|
+
if (apiToken) {
|
|
157
|
+
this.candidates.push({
|
|
158
|
+
name: null,
|
|
159
|
+
digest: sha256(apiToken),
|
|
160
|
+
scopes: new Set(['admin'])
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
for (const def of apiKeys ?? []) {
|
|
164
|
+
this.addKeyDefinition(def, apiToken);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
addKeyDefinition(def, apiToken) {
|
|
168
|
+
if (!def || typeof def.name !== 'string' || def.name.trim() === '') {
|
|
169
|
+
throw new errors_1.BeignetError('INVALID_PARAMS', 'apiKeys entries require a non-empty name');
|
|
170
|
+
}
|
|
171
|
+
if (typeof def.key !== 'string' || def.key === '') {
|
|
172
|
+
throw new errors_1.BeignetError('INVALID_PARAMS', `apiKeys entry "${def.name}" requires a non-empty key`);
|
|
173
|
+
}
|
|
174
|
+
if (!Array.isArray(def.scopes) ||
|
|
175
|
+
def.scopes.length === 0 ||
|
|
176
|
+
def.scopes.some((s) => !exports.API_SCOPES.has(s))) {
|
|
177
|
+
throw new errors_1.BeignetError('INVALID_PARAMS', `apiKeys entry "${def.name}" requires scopes from: readonly, invoice, admin`);
|
|
178
|
+
}
|
|
179
|
+
if (this.keyNames.includes(def.name)) {
|
|
180
|
+
throw new errors_1.BeignetError('INVALID_PARAMS', `Duplicate apiKeys name "${def.name}"`);
|
|
181
|
+
}
|
|
182
|
+
if (apiToken !== undefined && def.key === apiToken) {
|
|
183
|
+
throw new errors_1.BeignetError('INVALID_PARAMS', `apiKeys entry "${def.name}" duplicates the legacy apiToken value`);
|
|
184
|
+
}
|
|
185
|
+
const digest = sha256(def.key);
|
|
186
|
+
for (const existing of this.candidates) {
|
|
187
|
+
if (existing.name !== null && (0, crypto_1.timingSafeEqual)(existing.digest, digest)) {
|
|
188
|
+
throw new errors_1.BeignetError('INVALID_PARAMS', `apiKeys entry "${def.name}" duplicates the key of "${existing.name}"`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
this.keyNames.push(def.name);
|
|
192
|
+
this.candidates.push({
|
|
193
|
+
name: def.name,
|
|
194
|
+
digest,
|
|
195
|
+
scopes: new Set(def.scopes)
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
get enabled() {
|
|
199
|
+
return this.candidates.length > 0;
|
|
200
|
+
}
|
|
201
|
+
authenticate(header) {
|
|
202
|
+
if (!header)
|
|
203
|
+
return { ok: false };
|
|
204
|
+
const match = header.match(/^bearer\s+(.+)$/i);
|
|
205
|
+
if (!match)
|
|
206
|
+
return { ok: false };
|
|
207
|
+
const presented = sha256(match[1]);
|
|
208
|
+
let matched = null;
|
|
209
|
+
for (const candidate of this.candidates) {
|
|
210
|
+
const equal = (0, crypto_1.timingSafeEqual)(candidate.digest, presented);
|
|
211
|
+
if (equal &&
|
|
212
|
+
matched === null &&
|
|
213
|
+
(candidate.name === null || !this.revoked.has(candidate.name))) {
|
|
214
|
+
matched = candidate;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (!matched)
|
|
218
|
+
return { ok: false };
|
|
219
|
+
return { ok: true, keyName: matched.name, scopes: matched.scopes };
|
|
220
|
+
}
|
|
221
|
+
revoke(name) {
|
|
222
|
+
if (!this.keyNames.includes(name))
|
|
223
|
+
return false;
|
|
224
|
+
this.revoked.add(name);
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
listKeys() {
|
|
228
|
+
return this.candidates
|
|
229
|
+
.filter((c) => c.name !== null)
|
|
230
|
+
.map((c) => ({
|
|
231
|
+
name: c.name,
|
|
232
|
+
scopes: [...c.scopes],
|
|
233
|
+
revoked: this.revoked.has(c.name)
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exports.ApiKeyAuthenticator = ApiKeyAuthenticator;
|
|
238
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/cli/auth.ts"],"names":[],"mappings":"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"}
|