lightning-agent 0.1.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.
- package/README.md +201 -87
- package/bin/lightning-agent.js +19 -0
- package/lib/auth.js +380 -0
- package/lib/escrow.js +332 -0
- package/lib/index.js +20 -2
- package/lib/stream.js +477 -0
- package/lib/wallet.js +81 -0
- package/package.json +3 -3
- package/test-v030.js +282 -0
- package/test.js +37 -9
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# ⚡ lightning-agent
|
|
2
2
|
|
|
3
|
-
Lightning
|
|
3
|
+
Lightning toolkit for AI agents. Payments, auth, escrow, and streaming micropayments.
|
|
4
4
|
|
|
5
|
-
A
|
|
5
|
+
A SDK that gives AI agents the ability to transact, authenticate, escrow work, and stream content for sats — all over [Nostr Wallet Connect (NWC)](https://nwc.dev). No browser, no UI, no bank accounts. Connect to any NWC-compatible wallet (Alby Hub, Mutiny, etc.) and start building the agent economy.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -10,7 +10,18 @@ A tiny SDK that gives any AI agent the ability to send and receive Bitcoin Light
|
|
|
10
10
|
npm install lightning-agent
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## What's in the box
|
|
14
|
+
|
|
15
|
+
| Module | What it does | Since |
|
|
16
|
+
|--------|-------------|-------|
|
|
17
|
+
| **Wallet** | Send/receive Lightning payments, decode invoices | v0.1.0 |
|
|
18
|
+
| **Auth** | LNURL-auth — login with your Lightning wallet | v0.3.0 |
|
|
19
|
+
| **Escrow** | Hold funds until work is verified, then release or refund | v0.3.0 |
|
|
20
|
+
| **Stream** | Pay-per-token streaming micropayments | v0.3.0 |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Quick Start: Payments
|
|
14
25
|
|
|
15
26
|
```javascript
|
|
16
27
|
const { createWallet } = require('lightning-agent');
|
|
@@ -19,134 +30,248 @@ const wallet = createWallet('nostr+walletconnect://...');
|
|
|
19
30
|
|
|
20
31
|
// Check balance
|
|
21
32
|
const { balanceSats } = await wallet.getBalance();
|
|
22
|
-
console.log(`Balance: ${balanceSats} sats`);
|
|
23
33
|
|
|
24
34
|
// Create an invoice (get paid)
|
|
25
35
|
const { invoice, paymentHash } = await wallet.createInvoice({
|
|
26
36
|
amountSats: 50,
|
|
27
37
|
description: 'Text generation query'
|
|
28
38
|
});
|
|
29
|
-
console.log(`Pay me: ${invoice}`);
|
|
30
39
|
|
|
31
40
|
// Wait for payment
|
|
32
|
-
const { paid } = await wallet.waitForPayment(paymentHash
|
|
41
|
+
const { paid } = await wallet.waitForPayment(paymentHash);
|
|
33
42
|
|
|
34
|
-
// Pay an invoice
|
|
43
|
+
// Pay an invoice
|
|
35
44
|
const { preimage } = await wallet.payInvoice(someInvoice);
|
|
36
|
-
console.log(`Paid! Preimage: ${preimage}`);
|
|
37
45
|
|
|
38
|
-
//
|
|
46
|
+
// Pay a Lightning address directly
|
|
47
|
+
await wallet.payAddress('alice@getalby.com', { amountSats: 10 });
|
|
48
|
+
|
|
39
49
|
wallet.close();
|
|
40
50
|
```
|
|
41
51
|
|
|
42
|
-
|
|
52
|
+
---
|
|
43
53
|
|
|
44
|
-
|
|
54
|
+
## Auth (LNURL-auth)
|
|
55
|
+
|
|
56
|
+
Login with a Lightning wallet. No passwords, no OAuth — just a signed cryptographic challenge.
|
|
45
57
|
|
|
46
|
-
|
|
58
|
+
### Server side
|
|
47
59
|
|
|
48
60
|
```javascript
|
|
49
|
-
const
|
|
50
|
-
// or
|
|
51
|
-
process.env.NWC_URL = 'nostr+walletconnect://...';
|
|
52
|
-
const wallet = createWallet();
|
|
53
|
-
```
|
|
61
|
+
const { createAuthServer, signAuth } = require('lightning-agent');
|
|
54
62
|
|
|
55
|
-
|
|
63
|
+
const auth = createAuthServer({
|
|
64
|
+
callbackUrl: 'https://api.example.com/auth',
|
|
65
|
+
challengeTtlMs: 300000 // 5 minutes
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Generate a challenge
|
|
69
|
+
const { k1, lnurl } = auth.createChallenge();
|
|
70
|
+
// → lnurl can be rendered as QR for wallet apps
|
|
71
|
+
// → k1 can be sent directly to agent clients
|
|
72
|
+
|
|
73
|
+
// Verify a signed response
|
|
74
|
+
const result = auth.verify(k1, sig, key);
|
|
75
|
+
// → { valid: true, pubkey: '03abc...' }
|
|
76
|
+
|
|
77
|
+
// Or use as Express middleware
|
|
78
|
+
app.get('/auth', auth.middleware((pubkey, req, res) => {
|
|
79
|
+
console.log('Authenticated:', pubkey);
|
|
80
|
+
}));
|
|
81
|
+
```
|
|
56
82
|
|
|
57
|
-
|
|
83
|
+
### Client side (agent)
|
|
58
84
|
|
|
59
85
|
```javascript
|
|
60
|
-
const {
|
|
86
|
+
const { signAuth, authenticate } = require('lightning-agent');
|
|
87
|
+
|
|
88
|
+
// Sign a challenge manually
|
|
89
|
+
const { sig, key } = signAuth(k1, myPrivateKeyHex);
|
|
90
|
+
// → Send sig + key back to server
|
|
91
|
+
|
|
92
|
+
// Or complete the full flow automatically
|
|
93
|
+
const result = await authenticate('lnurl1...', myPrivateKeyHex);
|
|
94
|
+
if (result.success) console.log('Logged in as', result.pubkey);
|
|
61
95
|
```
|
|
62
96
|
|
|
63
|
-
|
|
97
|
+
### API
|
|
98
|
+
|
|
99
|
+
#### `createAuthServer(opts?)`
|
|
100
|
+
- `opts.challengeTtlMs` — Challenge validity in ms (default 300000)
|
|
101
|
+
- `opts.callbackUrl` — Full URL for LNURL generation
|
|
64
102
|
|
|
65
|
-
|
|
103
|
+
Returns: `{ createChallenge(), verify(k1, sig, key), middleware(onAuth), activeChallenges }`
|
|
66
104
|
|
|
67
|
-
|
|
105
|
+
#### `signAuth(k1, privateKey)`
|
|
106
|
+
Sign a challenge with a secp256k1 private key. Returns `{ sig, key }` (DER signature + compressed pubkey).
|
|
107
|
+
|
|
108
|
+
#### `authenticate(lnurlOrUrl, privateKey)`
|
|
109
|
+
Complete an LNURL-auth flow: fetch challenge → sign → submit. Returns `{ success, pubkey, error? }`.
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Escrow
|
|
114
|
+
|
|
115
|
+
Hold funds until work is verified. Client pays into escrow → worker delivers → escrow releases payment. If the worker doesn't deliver, funds are refunded.
|
|
68
116
|
|
|
69
117
|
```javascript
|
|
70
|
-
const {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
118
|
+
const { createWallet, createEscrowManager } = require('lightning-agent');
|
|
119
|
+
|
|
120
|
+
const escrowWallet = createWallet('nostr+walletconnect://...');
|
|
121
|
+
const mgr = createEscrowManager(escrowWallet, {
|
|
122
|
+
onStateChange: (id, from, to) => console.log(`${id}: ${from} → ${to}`)
|
|
75
123
|
});
|
|
76
|
-
```
|
|
77
124
|
|
|
78
|
-
|
|
125
|
+
// 1. Create escrow
|
|
126
|
+
const escrow = await mgr.create({
|
|
127
|
+
amountSats: 500,
|
|
128
|
+
workerAddress: 'worker@getalby.com',
|
|
129
|
+
description: 'Translate 200 words EN→ES',
|
|
130
|
+
deadlineMs: 3600000 // 1 hour
|
|
131
|
+
});
|
|
132
|
+
console.log('Client should pay:', escrow.invoice);
|
|
79
133
|
|
|
80
|
-
|
|
134
|
+
// 2. Confirm funding (waits for payment)
|
|
135
|
+
await mgr.fund(escrow.id);
|
|
136
|
+
|
|
137
|
+
// 3. Worker delivers proof
|
|
138
|
+
mgr.deliver(escrow.id, { url: 'https://example.com/result', hash: 'sha256...' });
|
|
139
|
+
|
|
140
|
+
// 4a. Release to worker
|
|
141
|
+
await mgr.release(escrow.id);
|
|
142
|
+
|
|
143
|
+
// 4b. Or refund to client
|
|
144
|
+
await mgr.refund(escrow.id, 'client@getalby.com', 'Worker no-show');
|
|
145
|
+
|
|
146
|
+
// 4c. Or dispute
|
|
147
|
+
mgr.dispute(escrow.id, 'Quality insufficient', 'client');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Escrow states
|
|
81
151
|
|
|
82
|
-
```javascript
|
|
83
|
-
const { preimage, paymentHash } = await wallet.payInvoice('lnbc50u1p...');
|
|
84
152
|
```
|
|
153
|
+
CREATED → FUNDED → DELIVERED → RELEASED
|
|
154
|
+
→ REFUNDED
|
|
155
|
+
→ EXPIRED (auto, on deadline)
|
|
156
|
+
→ DISPUTED
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### API
|
|
85
160
|
|
|
86
|
-
|
|
161
|
+
#### `createEscrowManager(wallet, opts?)`
|
|
162
|
+
- `wallet` — NWC wallet that holds escrowed funds
|
|
163
|
+
- `opts.onStateChange(id, oldState, newState, escrow)` — State change callback
|
|
164
|
+
- `opts.defaultDeadlineMs` — Default deadline (default 3600000)
|
|
87
165
|
|
|
88
|
-
|
|
166
|
+
Returns: `{ create(config), fund(id, opts?), deliver(id, proof), release(id), refund(id, address, reason?), dispute(id, reason, raisedBy), get(id), list(state?), close() }`
|
|
89
167
|
|
|
90
|
-
|
|
168
|
+
#### `EscrowState`
|
|
169
|
+
Enum: `CREATED`, `FUNDED`, `DELIVERED`, `RELEASED`, `REFUNDED`, `EXPIRED`, `DISPUTED`
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Streaming Payments
|
|
174
|
+
|
|
175
|
+
Pay-per-token micropayments. Lightning is the only payment system that can handle fractions-of-a-cent per word.
|
|
176
|
+
|
|
177
|
+
### Provider (server)
|
|
91
178
|
|
|
92
179
|
```javascript
|
|
93
|
-
const {
|
|
94
|
-
|
|
95
|
-
|
|
180
|
+
const { createWallet, createStreamProvider } = require('lightning-agent');
|
|
181
|
+
const http = require('http');
|
|
182
|
+
|
|
183
|
+
const wallet = createWallet('nostr+walletconnect://...');
|
|
184
|
+
const provider = createStreamProvider(wallet, {
|
|
185
|
+
satsPerBatch: 2, // charge 2 sats per batch
|
|
186
|
+
tokensPerBatch: 100, // 100 tokens per batch
|
|
187
|
+
maxBatches: 50 // cap at 50 batches (100 sats max)
|
|
96
188
|
});
|
|
97
|
-
```
|
|
98
189
|
|
|
99
|
-
|
|
190
|
+
http.createServer(async (req, res) => {
|
|
191
|
+
await provider.handleRequest(req, res, async function* () {
|
|
192
|
+
// Your generator yields tokens/strings
|
|
193
|
+
for (const word of myTextGenerator(req)) {
|
|
194
|
+
yield word + ' ';
|
|
195
|
+
}
|
|
196
|
+
}, { firstBatchFree: true });
|
|
197
|
+
}).listen(8080);
|
|
198
|
+
```
|
|
100
199
|
|
|
101
|
-
|
|
200
|
+
### Client (consumer)
|
|
102
201
|
|
|
103
202
|
```javascript
|
|
104
|
-
const {
|
|
105
|
-
|
|
203
|
+
const { createWallet, createStreamClient } = require('lightning-agent');
|
|
204
|
+
|
|
205
|
+
const wallet = createWallet('nostr+walletconnect://...');
|
|
206
|
+
const client = createStreamClient(wallet, { maxSats: 200 });
|
|
207
|
+
|
|
208
|
+
for await (const text of client.stream('https://api.example.com/generate', {
|
|
209
|
+
body: { prompt: 'Explain Lightning Network in 500 words' },
|
|
210
|
+
maxSats: 100 // budget for this stream
|
|
211
|
+
})) {
|
|
212
|
+
process.stdout.write(text);
|
|
213
|
+
}
|
|
106
214
|
```
|
|
107
215
|
|
|
108
|
-
###
|
|
216
|
+
### SSE Protocol
|
|
109
217
|
|
|
110
|
-
|
|
218
|
+
The provider uses Server-Sent Events:
|
|
111
219
|
|
|
112
|
-
|
|
220
|
+
```
|
|
221
|
+
event: session data: { "sessionId": "abc..." }
|
|
222
|
+
event: content data: { "tokens": "Hello world...", "batchIndex": 1 }
|
|
223
|
+
event: invoice data: { "invoice": "lnbc...", "sats": 2, "batchIndex": 2 }
|
|
224
|
+
event: content data: { "tokens": "more text...", "batchIndex": 2 }
|
|
225
|
+
event: done data: { "totalBatches": 5, "totalSats": 8, "totalTokens": 500 }
|
|
226
|
+
```
|
|
113
227
|
|
|
114
|
-
|
|
228
|
+
Client proves payment by POSTing `{ sessionId, preimage }` to the same URL.
|
|
115
229
|
|
|
116
|
-
|
|
117
|
-
const { decodeBolt11 } = require('lightning-agent');
|
|
118
|
-
const { amountSats } = decodeBolt11('lnbc210n1p...');
|
|
119
|
-
// amountSats = 21
|
|
120
|
-
```
|
|
230
|
+
### API
|
|
121
231
|
|
|
122
|
-
|
|
232
|
+
#### `createStreamProvider(wallet, opts?)`
|
|
233
|
+
- `opts.satsPerBatch` — Sats per batch (default 1)
|
|
234
|
+
- `opts.tokensPerBatch` — Tokens per batch (default 50)
|
|
235
|
+
- `opts.maxBatches` — Max batches per stream (default 100)
|
|
236
|
+
- `opts.paymentTimeoutMs` — Payment wait timeout (default 30000)
|
|
123
237
|
|
|
124
|
-
|
|
238
|
+
Returns: `{ handleRequest(req, res, generator, opts?), activeSessions }`
|
|
125
239
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
240
|
+
#### `createStreamClient(wallet, opts?)`
|
|
241
|
+
- `opts.maxSats` — Budget cap (default 1000)
|
|
242
|
+
- `opts.autoPay` — Auto-pay invoices (default true)
|
|
243
|
+
|
|
244
|
+
Returns: `{ stream(url, opts?), budget }`
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Wallet API Reference
|
|
249
|
+
|
|
250
|
+
### `createWallet(nwcUrl?)`
|
|
251
|
+
Create a wallet instance. Pass NWC URL directly or set `NWC_URL` env var.
|
|
252
|
+
|
|
253
|
+
### `wallet.getBalance(opts?)` → `{ balanceSats, balanceMsats }`
|
|
254
|
+
### `wallet.createInvoice(opts)` → `{ invoice, paymentHash, amountSats }`
|
|
255
|
+
### `wallet.payInvoice(invoice, opts?)` → `{ preimage, paymentHash }`
|
|
256
|
+
### `wallet.payAddress(address, opts)` → `{ preimage, paymentHash, invoice, amountSats }`
|
|
257
|
+
### `wallet.waitForPayment(hash, opts?)` → `{ paid, preimage, settledAt }`
|
|
258
|
+
### `wallet.decodeInvoice(invoice)` → `{ amountSats, network }`
|
|
259
|
+
### `wallet.close()`
|
|
260
|
+
|
|
261
|
+
### Standalone helpers
|
|
262
|
+
- `resolveLightningAddress(address, amountSats, comment?)` — Resolve without paying
|
|
263
|
+
- `decodeBolt11(invoice)` — Offline bolt11 decoder
|
|
264
|
+
- `parseNwcUrl(url)` — Parse NWC URL into components
|
|
130
265
|
|
|
131
266
|
## CLI
|
|
132
267
|
|
|
133
268
|
```bash
|
|
134
|
-
# Set your NWC URL
|
|
135
269
|
export NWC_URL="nostr+walletconnect://..."
|
|
136
270
|
|
|
137
|
-
# Check balance
|
|
138
271
|
lightning-agent balance
|
|
139
|
-
|
|
140
|
-
# Create an invoice for 50 sats
|
|
141
|
-
lightning-agent invoice 50 "Text generation query"
|
|
142
|
-
|
|
143
|
-
# Pay an invoice
|
|
272
|
+
lightning-agent invoice 50 "API call fee"
|
|
144
273
|
lightning-agent pay lnbc50u1p...
|
|
145
|
-
|
|
146
|
-
# Decode an invoice (offline)
|
|
147
274
|
lightning-agent decode lnbc50u1p...
|
|
148
|
-
|
|
149
|
-
# Wait for a payment
|
|
150
275
|
lightning-agent wait <payment_hash> [timeout_ms]
|
|
151
276
|
```
|
|
152
277
|
|
|
@@ -154,31 +279,20 @@ lightning-agent wait <payment_hash> [timeout_ms]
|
|
|
154
279
|
|
|
155
280
|
You need a Nostr Wallet Connect URL from a compatible wallet:
|
|
156
281
|
|
|
157
|
-
- **[Alby Hub](https://albyhub.com)** — Self-hosted Lightning node with NWC. Recommended
|
|
282
|
+
- **[Alby Hub](https://albyhub.com)** — Self-hosted Lightning node with NWC. Recommended.
|
|
158
283
|
- **[Mutiny Wallet](https://mutinywallet.com)** — Mobile-first with NWC support.
|
|
159
284
|
- **[Coinos](https://coinos.io)** — Web wallet with NWC.
|
|
160
285
|
|
|
161
|
-
The URL looks like: `nostr+walletconnect://<wallet_pubkey>?relay=wss://...&secret=<hex>`
|
|
162
|
-
|
|
163
|
-
## How It Works
|
|
164
|
-
|
|
165
|
-
lightning-agent uses the [NWC protocol (NIP-47)](https://github.com/nostr-protocol/nips/blob/master/47.md):
|
|
166
|
-
|
|
167
|
-
1. Your agent signs NWC requests (kind 23194) with the secret from the NWC URL
|
|
168
|
-
2. Requests are encrypted with NIP-04 and sent to the wallet's relay
|
|
169
|
-
3. The wallet service processes the request and returns an encrypted response (kind 23195)
|
|
170
|
-
4. All communication happens over Nostr relays — no direct connection to the wallet needed
|
|
171
|
-
|
|
172
286
|
## Design Philosophy
|
|
173
287
|
|
|
174
|
-
|
|
288
|
+
Built for AI agents, not humans:
|
|
175
289
|
|
|
176
290
|
- **Minimal deps** — just `nostr-tools` and `ws`
|
|
177
|
-
- **No UI** — pure code,
|
|
178
|
-
- **
|
|
291
|
+
- **No UI** — pure code, any Node.js environment
|
|
292
|
+
- **Fresh connections** — new relay connection per request for reliability
|
|
179
293
|
- **Timeouts everywhere** — agents can't afford to hang
|
|
180
|
-
- **
|
|
294
|
+
- **Composable** — auth + escrow + streaming + payments work together
|
|
181
295
|
|
|
182
296
|
## License
|
|
183
297
|
|
|
184
|
-
MIT
|
|
298
|
+
MIT — Built by [Jeletor](https://jeletor.com)
|
package/bin/lightning-agent.js
CHANGED
|
@@ -10,6 +10,7 @@ Usage:
|
|
|
10
10
|
lightning-agent balance Check wallet balance
|
|
11
11
|
lightning-agent invoice <sats> [description] Create an invoice
|
|
12
12
|
lightning-agent pay <bolt11> Pay an invoice
|
|
13
|
+
lightning-agent send <address> <sats> Pay a Lightning address
|
|
13
14
|
lightning-agent decode <bolt11> Decode an invoice (offline)
|
|
14
15
|
lightning-agent wait <payment_hash> [timeout] Wait for payment
|
|
15
16
|
|
|
@@ -21,6 +22,7 @@ Examples:
|
|
|
21
22
|
lightning-agent balance
|
|
22
23
|
lightning-agent invoice 50 "AI query fee"
|
|
23
24
|
lightning-agent pay lnbc50u1p...
|
|
25
|
+
lightning-agent send alice@getalby.com 100
|
|
24
26
|
lightning-agent decode lnbc50u1p...
|
|
25
27
|
`.trim();
|
|
26
28
|
|
|
@@ -99,6 +101,23 @@ async function main() {
|
|
|
99
101
|
break;
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
case 'send': {
|
|
105
|
+
const address = args[1];
|
|
106
|
+
const sats = parseInt(args[2], 10);
|
|
107
|
+
if (!address || !address.includes('@')) {
|
|
108
|
+
console.error('Error: Lightning address required (user@domain)');
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
if (!sats || sats <= 0) {
|
|
112
|
+
console.error('Error: amount in sats required (positive integer)');
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
console.error(`Sending ${sats} sats to ${address}...`);
|
|
116
|
+
const result = await wallet.payAddress(address, { amountSats: sats });
|
|
117
|
+
console.log(`Paid! Preimage: ${result.preimage}`);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
|
|
102
121
|
case 'wait': {
|
|
103
122
|
const paymentHash = args[1];
|
|
104
123
|
if (!paymentHash) {
|