lightning-agent 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,158 +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
45
 
37
- // Or pay a Lightning address directly
46
+ // Pay a Lightning address directly
38
47
  await wallet.payAddress('alice@getalby.com', { amountSats: 10 });
39
48
 
40
- // Done
41
49
  wallet.close();
42
50
  ```
43
51
 
44
- ## API Reference
52
+ ---
45
53
 
46
- ### `createWallet(nwcUrl?)`
54
+ ## Auth (LNURL-auth)
47
55
 
48
- Create a wallet instance. Pass an NWC URL directly or set the `NWC_URL` environment variable.
56
+ Login with a Lightning wallet. No passwords, no OAuth just a signed cryptographic challenge.
57
+
58
+ ### Server side
49
59
 
50
60
  ```javascript
51
- const wallet = createWallet('nostr+walletconnect://...');
52
- // or
53
- process.env.NWC_URL = 'nostr+walletconnect://...';
54
- const wallet = createWallet();
55
- ```
61
+ const { createAuthServer, signAuth } = require('lightning-agent');
62
+
63
+ const auth = createAuthServer({
64
+ callbackUrl: 'https://api.example.com/auth',
65
+ challengeTtlMs: 300000 // 5 minutes
66
+ });
56
67
 
57
- ### `wallet.getBalance(opts?)`
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
58
72
 
59
- Get the wallet balance.
73
+ // Verify a signed response
74
+ const result = auth.verify(k1, sig, key);
75
+ // → { valid: true, pubkey: '03abc...' }
60
76
 
61
- ```javascript
62
- const { balanceSats, balanceMsats } = await wallet.getBalance();
77
+ // Or use as Express middleware
78
+ app.get('/auth', auth.middleware((pubkey, req, res) => {
79
+ console.log('Authenticated:', pubkey);
80
+ }));
63
81
  ```
64
82
 
65
- **Options:** `{ timeoutMs: 15000 }`
83
+ ### Client side (agent)
66
84
 
67
- ### `wallet.createInvoice(opts)`
85
+ ```javascript
86
+ const { signAuth, authenticate } = require('lightning-agent');
68
87
 
69
- Create a Lightning invoice (receive payment).
88
+ // Sign a challenge manually
89
+ const { sig, key } = signAuth(k1, myPrivateKeyHex);
90
+ // → Send sig + key back to server
70
91
 
71
- ```javascript
72
- const { invoice, paymentHash, amountSats } = await wallet.createInvoice({
73
- amountSats: 100,
74
- description: 'API call fee',
75
- expiry: 3600, // optional, seconds
76
- timeoutMs: 15000 // optional
77
- });
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);
78
95
  ```
79
96
 
80
- ### `wallet.payInvoice(invoice, opts?)`
97
+ ### API
81
98
 
82
- Pay a Lightning invoice.
99
+ #### `createAuthServer(opts?)`
100
+ - `opts.challengeTtlMs` — Challenge validity in ms (default 300000)
101
+ - `opts.callbackUrl` — Full URL for LNURL generation
83
102
 
84
- ```javascript
85
- const { preimage, paymentHash } = await wallet.payInvoice('lnbc50u1p...');
86
- ```
103
+ Returns: `{ createChallenge(), verify(k1, sig, key), middleware(onAuth), activeChallenges }`
104
+
105
+ #### `signAuth(k1, privateKey)`
106
+ Sign a challenge with a secp256k1 private key. Returns `{ sig, key }` (DER signature + compressed pubkey).
87
107
 
88
- **Options:** `{ timeoutMs: 30000 }`
108
+ #### `authenticate(lnurlOrUrl, privateKey)`
109
+ Complete an LNURL-auth flow: fetch challenge → sign → submit. Returns `{ success, pubkey, error? }`.
89
110
 
90
- ### `wallet.payAddress(address, opts)`
111
+ ---
91
112
 
92
- Pay a Lightning address (user@domain) via LNURL-pay. Resolves the address to an invoice and pays it in one call.
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.
93
116
 
94
117
  ```javascript
95
- const result = await wallet.payAddress('alice@getalby.com', {
96
- amountSats: 100,
97
- comment: 'Great work!', // optional
98
- timeoutMs: 30000 // 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}`)
99
123
  });
100
- // { preimage, paymentHash, invoice, amountSats }
101
- ```
102
124
 
103
- ### `wallet.waitForPayment(paymentHash, 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);
104
133
 
105
- Poll until an invoice is paid (or timeout).
134
+ // 2. Confirm funding (waits for payment)
135
+ await mgr.fund(escrow.id);
106
136
 
107
- ```javascript
108
- const { paid, preimage, settledAt } = await wallet.waitForPayment(hash, {
109
- timeoutMs: 60000, // total wait (default 60s)
110
- pollIntervalMs: 2000 // poll frequency (default 2s)
111
- });
112
- ```
137
+ // 3. Worker delivers proof
138
+ mgr.deliver(escrow.id, { url: 'https://example.com/result', hash: 'sha256...' });
113
139
 
114
- ### `wallet.decodeInvoice(invoice)`
140
+ // 4a. Release to worker
141
+ await mgr.release(escrow.id);
115
142
 
116
- Decode a bolt11 invoice offline (no wallet connection needed). Extracts amount and network.
143
+ // 4b. Or refund to client
144
+ await mgr.refund(escrow.id, 'client@getalby.com', 'Worker no-show');
117
145
 
118
- ```javascript
119
- const { amountSats, network } = wallet.decodeInvoice('lnbc50u1p...');
120
- // { amountSats: 5000, network: 'mainnet', description: null, paymentHash: null }
146
+ // 4c. Or dispute
147
+ mgr.dispute(escrow.id, 'Quality insufficient', 'client');
121
148
  ```
122
149
 
123
- ### `wallet.close()`
150
+ ### Escrow states
151
+
152
+ ```
153
+ CREATED → FUNDED → DELIVERED → RELEASED
154
+ → REFUNDED
155
+ → EXPIRED (auto, on deadline)
156
+ → DISPUTED
157
+ ```
158
+
159
+ ### API
160
+
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)
165
+
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() }`
124
167
 
125
- Close the relay connection. Call when done.
168
+ #### `EscrowState`
169
+ Enum: `CREATED`, `FUNDED`, `DELIVERED`, `RELEASED`, `REFUNDED`, `EXPIRED`, `DISPUTED`
126
170
 
127
- ### `resolveLightningAddress(address, amountSats, comment?)`
171
+ ---
128
172
 
129
- Resolve a Lightning address to a bolt11 invoice without paying (useful for inspection).
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)
130
178
 
131
179
  ```javascript
132
- const { resolveLightningAddress } = require('lightning-agent');
133
- const { invoice, minSats, maxSats } = await resolveLightningAddress('bob@walletofsatoshi.com', 50);
134
- ```
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)
188
+ });
135
189
 
136
- ### `decodeBolt11(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
+ ```
137
199
 
138
- Standalone bolt11 decoder (no wallet instance needed).
200
+ ### Client (consumer)
139
201
 
140
202
  ```javascript
141
- const { decodeBolt11 } = require('lightning-agent');
142
- const { amountSats } = decodeBolt11('lnbc210n1p...');
143
- // amountSats = 21
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
+ }
144
214
  ```
145
215
 
146
- ### `parseNwcUrl(url)`
216
+ ### SSE Protocol
147
217
 
148
- Parse an NWC URL into its components.
218
+ The provider uses Server-Sent Events:
149
219
 
150
- ```javascript
151
- const { parseNwcUrl } = require('lightning-agent');
152
- const { walletPubkey, relay, secret } = parseNwcUrl('nostr+walletconnect://...');
153
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
+ ```
227
+
228
+ Client proves payment by POSTing `{ sessionId, preimage }` to the same URL.
229
+
230
+ ### API
231
+
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)
237
+
238
+ Returns: `{ handleRequest(req, res, generator, opts?), activeSessions }`
239
+
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
154
265
 
155
266
  ## CLI
156
267
 
157
268
  ```bash
158
- # Set your NWC URL
159
269
  export NWC_URL="nostr+walletconnect://..."
160
270
 
161
- # Check balance
162
271
  lightning-agent balance
163
-
164
- # Create an invoice for 50 sats
165
- lightning-agent invoice 50 "Text generation query"
166
-
167
- # Pay an invoice
272
+ lightning-agent invoice 50 "API call fee"
168
273
  lightning-agent pay lnbc50u1p...
169
-
170
- # Decode an invoice (offline)
171
274
  lightning-agent decode lnbc50u1p...
172
-
173
- # Wait for a payment
174
275
  lightning-agent wait <payment_hash> [timeout_ms]
175
276
  ```
176
277
 
@@ -178,31 +279,20 @@ lightning-agent wait <payment_hash> [timeout_ms]
178
279
 
179
280
  You need a Nostr Wallet Connect URL from a compatible wallet:
180
281
 
181
- - **[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.
182
283
  - **[Mutiny Wallet](https://mutinywallet.com)** — Mobile-first with NWC support.
183
284
  - **[Coinos](https://coinos.io)** — Web wallet with NWC.
184
285
 
185
- The URL looks like: `nostr+walletconnect://<wallet_pubkey>?relay=wss://...&secret=<hex>`
186
-
187
- ## How It Works
188
-
189
- lightning-agent uses the [NWC protocol (NIP-47)](https://github.com/nostr-protocol/nips/blob/master/47.md):
190
-
191
- 1. Your agent signs NWC requests (kind 23194) with the secret from the NWC URL
192
- 2. Requests are encrypted with NIP-04 and sent to the wallet's relay
193
- 3. The wallet service processes the request and returns an encrypted response (kind 23195)
194
- 4. All communication happens over Nostr relays — no direct connection to the wallet needed
195
-
196
286
  ## Design Philosophy
197
287
 
198
- This is built for AI agents, not humans:
288
+ Built for AI agents, not humans:
199
289
 
200
290
  - **Minimal deps** — just `nostr-tools` and `ws`
201
- - **No UI** — pure code, works in any Node.js environment
202
- - **Reliable connections** — fresh relay connection per request for maximum reliability
291
+ - **No UI** — pure code, any Node.js environment
292
+ - **Fresh connections** — new relay connection per request for reliability
203
293
  - **Timeouts everywhere** — agents can't afford to hang
204
- - **Simple API** — `createInvoice` to charge, `payInvoice` to pay
294
+ - **Composable** — auth + escrow + streaming + payments work together
205
295
 
206
296
  ## License
207
297
 
208
- MIT
298
+ MIT — Built by [Jeletor](https://jeletor.com)