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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # ⚡ lightning-agent
2
2
 
3
- Lightning payments for AI agents. Two functions: charge and pay.
3
+ Lightning toolkit for AI agents. Payments, auth, escrow, and streaming micropayments.
4
4
 
5
- A tiny SDK that gives any AI agent the ability to send and receive Bitcoin Lightning payments using [Nostr Wallet Connect (NWC)](https://nwc.dev). No browser, no UI just code. Connect your agent to an NWC-compatible wallet (Alby Hub, Mutiny, etc.) and start transacting in sats.
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
- ## Quick Start
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, { timeoutMs: 60000 });
41
+ const { paid } = await wallet.waitForPayment(paymentHash);
33
42
 
34
- // Pay an invoice (spend)
43
+ // Pay an invoice
35
44
  const { preimage } = await wallet.payInvoice(someInvoice);
36
- console.log(`Paid! Preimage: ${preimage}`);
37
45
 
38
- // Done
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
- ## API Reference
52
+ ---
43
53
 
44
- ### `createWallet(nwcUrl?)`
54
+ ## Auth (LNURL-auth)
55
+
56
+ Login with a Lightning wallet. No passwords, no OAuth — just a signed cryptographic challenge.
45
57
 
46
- Create a wallet instance. Pass an NWC URL directly or set the `NWC_URL` environment variable.
58
+ ### Server side
47
59
 
48
60
  ```javascript
49
- const wallet = createWallet('nostr+walletconnect://...');
50
- // or
51
- process.env.NWC_URL = 'nostr+walletconnect://...';
52
- const wallet = createWallet();
53
- ```
61
+ const { createAuthServer, signAuth } = require('lightning-agent');
54
62
 
55
- ### `wallet.getBalance(opts?)`
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
- Get the wallet balance.
83
+ ### Client side (agent)
58
84
 
59
85
  ```javascript
60
- const { balanceSats, balanceMsats } = await wallet.getBalance();
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
- **Options:** `{ timeoutMs: 15000 }`
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
- ### `wallet.createInvoice(opts)`
103
+ Returns: `{ createChallenge(), verify(k1, sig, key), middleware(onAuth), activeChallenges }`
66
104
 
67
- Create a Lightning invoice (receive payment).
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 { invoice, paymentHash, amountSats } = await wallet.createInvoice({
71
- amountSats: 100,
72
- description: 'API call fee',
73
- expiry: 3600, // optional, seconds
74
- timeoutMs: 15000 // optional
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
- ### `wallet.payInvoice(invoice, opts?)`
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
- Pay a Lightning invoice.
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
- **Options:** `{ timeoutMs: 30000 }`
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
- ### `wallet.waitForPayment(paymentHash, opts?)`
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
- Poll until an invoice is paid (or timeout).
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 { paid, preimage, settledAt } = await wallet.waitForPayment(hash, {
94
- timeoutMs: 60000, // total wait (default 60s)
95
- pollIntervalMs: 2000 // poll frequency (default 2s)
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
- ### `wallet.decodeInvoice(invoice)`
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
- Decode a bolt11 invoice offline (no wallet connection needed). Extracts amount and network.
200
+ ### Client (consumer)
102
201
 
103
202
  ```javascript
104
- const { amountSats, network } = wallet.decodeInvoice('lnbc50u1p...');
105
- // { amountSats: 5000, network: 'mainnet', description: null, paymentHash: null }
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
- ### `wallet.close()`
216
+ ### SSE Protocol
109
217
 
110
- Close the relay connection. Call when done.
218
+ The provider uses Server-Sent Events:
111
219
 
112
- ### `decodeBolt11(invoice)`
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
- Standalone bolt11 decoder (no wallet instance needed).
228
+ Client proves payment by POSTing `{ sessionId, preimage }` to the same URL.
115
229
 
116
- ```javascript
117
- const { decodeBolt11 } = require('lightning-agent');
118
- const { amountSats } = decodeBolt11('lnbc210n1p...');
119
- // amountSats = 21
120
- ```
230
+ ### API
121
231
 
122
- ### `parseNwcUrl(url)`
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
- Parse an NWC URL into its components.
238
+ Returns: `{ handleRequest(req, res, generator, opts?), activeSessions }`
125
239
 
126
- ```javascript
127
- const { parseNwcUrl } = require('lightning-agent');
128
- const { walletPubkey, relay, secret } = parseNwcUrl('nostr+walletconnect://...');
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 for agents.
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
- This is built for AI agents, not humans:
288
+ Built for AI agents, not humans:
175
289
 
176
290
  - **Minimal deps** — just `nostr-tools` and `ws`
177
- - **No UI** — pure code, works in any Node.js environment
178
- - **Connection reuse** — maintains a single relay connection across requests
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
- - **Simple API** — `createInvoice` to charge, `payInvoice` to pay
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)
@@ -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) {