@thecryptodonkey/toll-booth 1.0.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.
Files changed (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/dist/adapters/express.d.ts +51 -0
  4. package/dist/adapters/express.d.ts.map +1 -0
  5. package/dist/adapters/express.js +237 -0
  6. package/dist/adapters/express.js.map +1 -0
  7. package/dist/adapters/proxy-headers.d.ts +7 -0
  8. package/dist/adapters/proxy-headers.d.ts.map +1 -0
  9. package/dist/adapters/proxy-headers.js +58 -0
  10. package/dist/adapters/proxy-headers.js.map +1 -0
  11. package/dist/adapters/web-standard.d.ts +60 -0
  12. package/dist/adapters/web-standard.d.ts.map +1 -0
  13. package/dist/adapters/web-standard.js +252 -0
  14. package/dist/adapters/web-standard.js.map +1 -0
  15. package/dist/backends/alby.d.ts +25 -0
  16. package/dist/backends/alby.d.ts.map +1 -0
  17. package/dist/backends/alby.js +137 -0
  18. package/dist/backends/alby.js.map +1 -0
  19. package/dist/backends/cln.d.ts +22 -0
  20. package/dist/backends/cln.d.ts.map +1 -0
  21. package/dist/backends/cln.js +55 -0
  22. package/dist/backends/cln.js.map +1 -0
  23. package/dist/backends/lnbits.d.ts +23 -0
  24. package/dist/backends/lnbits.d.ts.map +1 -0
  25. package/dist/backends/lnbits.js +58 -0
  26. package/dist/backends/lnbits.js.map +1 -0
  27. package/dist/backends/lnd.d.ts +21 -0
  28. package/dist/backends/lnd.d.ts.map +1 -0
  29. package/dist/backends/lnd.js +59 -0
  30. package/dist/backends/lnd.js.map +1 -0
  31. package/dist/backends/phoenixd.d.ts +19 -0
  32. package/dist/backends/phoenixd.d.ts.map +1 -0
  33. package/dist/backends/phoenixd.js +59 -0
  34. package/dist/backends/phoenixd.js.map +1 -0
  35. package/dist/booth.d.ts +54 -0
  36. package/dist/booth.d.ts.map +1 -0
  37. package/dist/booth.js +200 -0
  38. package/dist/booth.js.map +1 -0
  39. package/dist/core/cashu-redeem.d.ts +9 -0
  40. package/dist/core/cashu-redeem.d.ts.map +1 -0
  41. package/dist/core/cashu-redeem.js +85 -0
  42. package/dist/core/cashu-redeem.js.map +1 -0
  43. package/dist/core/create-invoice.d.ts +19 -0
  44. package/dist/core/create-invoice.d.ts.map +1 -0
  45. package/dist/core/create-invoice.js +66 -0
  46. package/dist/core/create-invoice.js.map +1 -0
  47. package/dist/core/invoice-status.d.ts +24 -0
  48. package/dist/core/invoice-status.d.ts.map +1 -0
  49. package/dist/core/invoice-status.js +74 -0
  50. package/dist/core/invoice-status.js.map +1 -0
  51. package/dist/core/nwc-pay.d.ts +8 -0
  52. package/dist/core/nwc-pay.d.ts.map +1 -0
  53. package/dist/core/nwc-pay.js +23 -0
  54. package/dist/core/nwc-pay.js.map +1 -0
  55. package/dist/core/toll-booth.d.ts +9 -0
  56. package/dist/core/toll-booth.d.ts.map +1 -0
  57. package/dist/core/toll-booth.js +172 -0
  58. package/dist/core/toll-booth.js.map +1 -0
  59. package/dist/core/types.d.ts +101 -0
  60. package/dist/core/types.d.ts.map +1 -0
  61. package/dist/core/types.js +3 -0
  62. package/dist/core/types.js.map +1 -0
  63. package/dist/free-tier.d.ts +14 -0
  64. package/dist/free-tier.d.ts.map +1 -0
  65. package/dist/free-tier.js +41 -0
  66. package/dist/free-tier.js.map +1 -0
  67. package/dist/index.d.ts +38 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +27 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/macaroon.d.ts +39 -0
  72. package/dist/macaroon.d.ts.map +1 -0
  73. package/dist/macaroon.js +111 -0
  74. package/dist/macaroon.js.map +1 -0
  75. package/dist/payment-page.d.ts +18 -0
  76. package/dist/payment-page.d.ts.map +1 -0
  77. package/dist/payment-page.js +391 -0
  78. package/dist/payment-page.js.map +1 -0
  79. package/dist/stats.d.ts +63 -0
  80. package/dist/stats.d.ts.map +1 -0
  81. package/dist/stats.js +75 -0
  82. package/dist/stats.js.map +1 -0
  83. package/dist/storage/interface.d.ts +58 -0
  84. package/dist/storage/interface.d.ts.map +1 -0
  85. package/dist/storage/interface.js +3 -0
  86. package/dist/storage/interface.js.map +1 -0
  87. package/dist/storage/memory.d.ts +3 -0
  88. package/dist/storage/memory.d.ts.map +1 -0
  89. package/dist/storage/memory.js +139 -0
  90. package/dist/storage/memory.js.map +1 -0
  91. package/dist/storage/sqlite.d.ts +6 -0
  92. package/dist/storage/sqlite.d.ts.map +1 -0
  93. package/dist/storage/sqlite.js +264 -0
  94. package/dist/storage/sqlite.js.map +1 -0
  95. package/dist/types.d.ts +198 -0
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +8 -0
  98. package/dist/types.js.map +1 -0
  99. package/llms.txt +91 -0
  100. package/package.json +100 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025–2026 TheCryptoDonkey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,205 @@
1
+ # toll-booth
2
+
3
+ [![MIT licence](https://img.shields.io/badge/licence-MIT-blue.svg)](./LICENSE)
4
+ [![Nostr](https://img.shields.io/badge/Nostr-Zap%20me-purple)](https://primal.net/p/npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2)
5
+
6
+ L402 Lightning payment middleware for Node.js. Gate any HTTP API behind a paywall with a single function call.
7
+
8
+ Supports **Express 5**, **Deno**, **Bun**, and **Cloudflare Workers** via the Web Standard adapter.
9
+
10
+ **[Why L402?](docs/vision.md)** — the case for permissionless, machine-to-machine payments on the web.
11
+
12
+ ## Features
13
+
14
+ - **L402 protocol** — industry-standard HTTP 402 payment flow with macaroon credentials
15
+ - **Multiple Lightning backends** — Phoenixd, LND, CLN, LNbits, Alby
16
+ - **Alternative payment methods** — Nostr Wallet Connect (NWC) and Cashu ecash tokens
17
+ - **Cashu-only mode** — no Lightning node required
18
+ - **Credit system** — pre-paid balance with volume discount tiers
19
+ - **Free tier** — configurable daily allowance per IP
20
+ - **Self-service payment page** — QR codes, tier selector, wallet adapter buttons
21
+ - **SQLite persistence** — WAL mode, automatic invoice expiry pruning
22
+ - **Framework-agnostic core** — use the \`Booth\` facade or wire handlers directly
23
+
24
+ ## Quick start
25
+
26
+ \`\`\`bash
27
+ npm install @thecryptodonkey/toll-booth
28
+ \`\`\`
29
+
30
+ ### Express
31
+
32
+ \`\`\`typescript
33
+ import express from 'express'
34
+ import { Booth } from '@thecryptodonkey/toll-booth'
35
+ import { phoenixdBackend } from '@thecryptodonkey/toll-booth/backends/phoenixd'
36
+
37
+ const app = express()
38
+ app.use(express.json())
39
+
40
+ const booth = new Booth({
41
+ adapter: 'express',
42
+ backend: phoenixdBackend({
43
+ url: 'http://localhost:9740',
44
+ password: process.env.PHOENIXD_PASSWORD!,
45
+ }),
46
+ pricing: { '/api': 10 }, // 10 sats per request
47
+ upstream: 'http://localhost:8080', // your API
48
+ rootKey: process.env.ROOT_KEY, // 64 hex chars, required for production
49
+ })
50
+
51
+ app.get('/invoice-status/:paymentHash', booth.invoiceStatusHandler as express.RequestHandler)
52
+ app.post('/create-invoice', booth.createInvoiceHandler as express.RequestHandler)
53
+ app.use('/', booth.middleware as express.RequestHandler)
54
+
55
+ app.listen(3000)
56
+ \`\`\`
57
+
58
+ ### Web Standard (Deno / Bun / Workers)
59
+
60
+ \`\`\`typescript
61
+ import { Booth } from '@thecryptodonkey/toll-booth'
62
+ import { lndBackend } from '@thecryptodonkey/toll-booth/backends/lnd'
63
+
64
+ const booth = new Booth({
65
+ adapter: 'web-standard',
66
+ backend: lndBackend({
67
+ url: 'https://localhost:8080',
68
+ macaroon: process.env.LND_MACAROON!,
69
+ }),
70
+ pricing: { '/api': 5 },
71
+ upstream: 'http://localhost:8080',
72
+ })
73
+
74
+ // Deno example
75
+ Deno.serve({ port: 3000 }, async (req: Request) => {
76
+ const url = new URL(req.url)
77
+ if (url.pathname.startsWith('/invoice-status/'))
78
+ return booth.invoiceStatusHandler(req)
79
+ if (url.pathname === '/create-invoice' && req.method === 'POST')
80
+ return booth.createInvoiceHandler(req)
81
+ return booth.middleware(req)
82
+ })
83
+ \`\`\`
84
+
85
+ ### Cashu-only (no Lightning node)
86
+
87
+ \`\`\`typescript
88
+ import { Booth } from '@thecryptodonkey/toll-booth'
89
+
90
+ const booth = new Booth({
91
+ adapter: 'web-standard',
92
+ redeemCashu: async (token, paymentHash) => {
93
+ // Verify and redeem the ecash token with your Cashu mint
94
+ // Return the amount redeemed in satoshis
95
+ return amountRedeemed
96
+ },
97
+ pricing: { '/api': 5 },
98
+ upstream: 'http://localhost:8080',
99
+ })
100
+ \`\`\`
101
+
102
+ No Lightning node, no channels, no liquidity management. Ideal for serverless and edge deployments.
103
+
104
+ ## Configuration
105
+
106
+ The \`Booth\` constructor accepts:
107
+
108
+ | Option | Type | Description |
109
+ |--------|------|-------------|
110
+ | \`adapter\` | \`'express' \| 'web-standard'\` | Framework integration to use |
111
+ | \`backend\` | \`LightningBackend\` | Lightning node (optional if using Cashu-only) |
112
+ | \`pricing\` | \`Record<string, number>\` | Route pattern → cost in sats |
113
+ | \`upstream\` | \`string\` | URL to proxy authorised requests to |
114
+ | \`rootKey\` | \`string\` | Macaroon signing key (64 hex chars). Random if omitted |
115
+ | \`dbPath\` | \`string\` | SQLite path. Default: \`./toll-booth.db\` |
116
+ | \`storage\` | \`StorageBackend\` | Custom storage (alternative to \`dbPath\`) |
117
+ | \`freeTier\` | \`{ requestsPerDay: number }\` | Daily free allowance per IP |
118
+ | \`strictPricing\` | \`boolean\` | Challenge unpriced routes instead of passing through |
119
+ | \`creditTiers\` | \`CreditTier[]\` | Volume discount tiers |
120
+ | \`trustProxy\` | \`boolean\` | Trust \`X-Forwarded-For\` / \`X-Real-IP\` |
121
+ | \`getClientIp\` | \`(req) => string\` | Custom IP resolver for non-standard runtimes |
122
+ | \`responseHeaders\` | \`Record<string, string>\` | Extra headers on every response |
123
+ | \`nwcPayInvoice\` | \`(uri, bolt11) => Promise<string>\` | NWC payment callback |
124
+ | \`redeemCashu\` | \`(token, hash) => Promise<number>\` | Cashu redemption callback |
125
+ | \`invoiceMaxAgeMs\` | \`number\` | Invoice pruning age. Default: 24h. \`0\` to disable |
126
+ | \`upstreamTimeout\` | \`number\` | Proxy timeout in ms. Default: 30s |
127
+
128
+ ## Lightning backends
129
+
130
+ \`\`\`typescript
131
+ import { phoenixdBackend } from '@thecryptodonkey/toll-booth/backends/phoenixd'
132
+ import { lndBackend } from '@thecryptodonkey/toll-booth/backends/lnd'
133
+ import { clnBackend } from '@thecryptodonkey/toll-booth/backends/cln'
134
+ import { lnbitsBackend } from '@thecryptodonkey/toll-booth/backends/lnbits'
135
+ import { albyBackend } from '@thecryptodonkey/toll-booth/backends/alby'
136
+ \`\`\`
137
+
138
+ Each backend implements the `LightningBackend` interface (`createInvoice` + `checkInvoice`).
139
+
140
+ | Backend | Status | Notes |
141
+ |---------|--------|-------|
142
+ | Phoenixd | Stable | Simplest self-hosted option |
143
+ | LND | Stable | Industry standard |
144
+ | CLN | Stable | Core Lightning REST API |
145
+ | LNbits | Stable | Any LNbits instance — self-hosted or hosted |
146
+ | Alby (NWC) | Experimental | JSON relay transport is unauthenticated; only enable with `allowInsecureRelay: true` for local testing or a fully trusted relay |
147
+
148
+ ## Subpath exports
149
+
150
+ Tree-shakeable imports for bundlers:
151
+
152
+ \`\`\`typescript
153
+ import { Booth } from '@thecryptodonkey/toll-booth'
154
+ import { phoenixdBackend } from '@thecryptodonkey/toll-booth/backends/phoenixd'
155
+ import { sqliteStorage } from '@thecryptodonkey/toll-booth/storage/sqlite'
156
+ import { memoryStorage } from '@thecryptodonkey/toll-booth/storage/memory'
157
+ import { createExpressMiddleware } from '@thecryptodonkey/toll-booth/adapters/express'
158
+ import { createWebStandardMiddleware } from '@thecryptodonkey/toll-booth/adapters/web-standard'
159
+ \`\`\`
160
+
161
+ ## Payment flow
162
+
163
+ 1. Client requests a priced endpoint without credentials
164
+ 2. Free tier checked — if allowance remains, request passes through
165
+ 3. If exhausted → **402** response with BOLT-11 invoice + macaroon
166
+ 4. Client pays via Lightning, NWC, or Cashu
167
+ 5. Client sends \`Authorization: L402 <macaroon>:<preimage>\`
168
+ 6. Macaroon verified, credit deducted, request proxied upstream
169
+
170
+ ## Why not Aperture?
171
+
172
+ [Aperture](https://github.com/lightninglabs/aperture) is Lightning Labs' production L402 reverse proxy. It's battle-tested and feature-rich. Use it if you can.
173
+
174
+ | | Aperture | toll-booth |
175
+ |---|---|---|
176
+ | **Language** | Go binary | TypeScript middleware |
177
+ | **Deployment** | Standalone reverse proxy | Embeds in your existing app |
178
+ | **Lightning node** | Requires LND | Phoenixd, LND, CLN, LNbits, or none (Cashu-only) |
179
+ | **Serverless** | No — long-running process | Yes — Web Standard adapter runs on Cloudflare Workers, Deno, Bun |
180
+ | **Configuration** | YAML file | Programmatic (code) |
181
+
182
+ ## Production checklist
183
+
184
+ - Set a persistent `rootKey` (64 hex chars / 32 bytes), otherwise tokens are invalidated on restart.
185
+ - Use a persistent `dbPath` (default: `./toll-booth.db`).
186
+ - Enable `strictPricing: true` to prevent unpriced routes from bypassing billing.
187
+ - Ensure your `pricing` keys match the paths the middleware actually sees (after mounting).
188
+ - Set `trustProxy: true` when behind a reverse proxy, or provide a `getClientIp` callback for per-client free-tier isolation.
189
+ - If you implement `redeemCashu`, make it idempotent for the same `paymentHash` — crash recovery depends on it.
190
+ - Rate-limit `/create-invoice` at your reverse proxy — each call creates a real Lightning invoice.
191
+
192
+ ## Example deployment
193
+
194
+ See [`examples/valhalla-proxy/`](examples/valhalla-proxy/) for a complete Docker Compose setup gating a [Valhalla](https://github.com/valhalla/valhalla) routing engine behind Lightning payments.
195
+
196
+ ## Support
197
+
198
+ If you find toll-booth useful, consider sending a tip:
199
+
200
+ - **Lightning:** `thedonkey@strike.me`
201
+ - **Nostr zaps:** `npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2`
202
+
203
+ ## Licence
204
+
205
+ [MIT](LICENSE)
@@ -0,0 +1,51 @@
1
+ import type { RequestHandler } from 'express';
2
+ import type { TollBoothEngine } from '../core/toll-booth.js';
3
+ import type { CreateInvoiceDeps } from '../core/create-invoice.js';
4
+ import type { InvoiceStatusDeps } from '../core/invoice-status.js';
5
+ import type { NwcPayDeps } from '../core/nwc-pay.js';
6
+ import type { CashuRedeemDeps } from '../core/cashu-redeem.js';
7
+ /**
8
+ * Returns an Express `RequestHandler` that enforces L402 payment gating.
9
+ *
10
+ * On `pass` or `proxy` results the request is forwarded to the upstream.
11
+ * On `challenge` a 402 response is returned with invoice details.
12
+ */
13
+ export interface ExpressMiddlewareConfig {
14
+ engine: TollBoothEngine;
15
+ upstream: string;
16
+ trustProxy?: boolean;
17
+ responseHeaders?: Record<string, string>;
18
+ /** Timeout in milliseconds for upstream proxy requests (default: 30000). */
19
+ upstreamTimeout?: number;
20
+ }
21
+ export declare function createExpressMiddleware(engineOrConfig: TollBoothEngine | ExpressMiddlewareConfig, upstreamArg?: string): RequestHandler;
22
+ /**
23
+ * Returns an Express `RequestHandler` that serves invoice status as JSON or HTML.
24
+ *
25
+ * Expects `:paymentHash` route param plus a `?token=...` status lookup secret.
26
+ * When `Accept: text/html` is requested, renders the self-service payment page;
27
+ * otherwise returns JSON with `{ paid, preimage }`.
28
+ */
29
+ export declare function createExpressInvoiceStatusHandler(deps: InvoiceStatusDeps): RequestHandler;
30
+ /**
31
+ * Returns an Express `RequestHandler` that creates a new Lightning invoice.
32
+ *
33
+ * Assumes `express.json()` middleware is already mounted. Delegates to the
34
+ * core `handleCreateInvoice` and returns the result.
35
+ */
36
+ export declare function createExpressCreateInvoiceHandler(deps: CreateInvoiceDeps): RequestHandler;
37
+ /**
38
+ * Returns an Express `RequestHandler` that pays a Lightning invoice via NWC.
39
+ *
40
+ * Expects JSON body with `{ nwcUri, bolt11, paymentHash, statusToken }`.
41
+ * Returns the payment preimage on success.
42
+ */
43
+ export declare function createExpressNwcHandler(deps: NwcPayDeps): RequestHandler;
44
+ /**
45
+ * Returns an Express `RequestHandler` that redeems a Cashu token as payment.
46
+ *
47
+ * Expects JSON body with `{ token, paymentHash, statusToken }`.
48
+ * Uses durable claims and leases to avoid concurrent duplicate redemption.
49
+ */
50
+ export declare function createExpressCashuHandler(deps: CashuRedeemDeps): RequestHandler;
51
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAA;AAC9E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAIlE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAW9D;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AA6CD,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,eAAe,GAAG,uBAAuB,EACzD,WAAW,CAAC,EAAE,MAAM,GACnB,cAAc,CA0GhB;AAID;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,iBAAiB,GACtB,cAAc,CA4ChB;AAID;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,iBAAiB,GACtB,cAAc,CAqBhB;AAID;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAUxE;AAID;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,eAAe,GAAG,cAAc,CAY/E"}
@@ -0,0 +1,237 @@
1
+ import { handleCreateInvoice } from '../core/create-invoice.js';
2
+ import { handleInvoiceStatus, renderInvoiceStatusHtml } from '../core/invoice-status.js';
3
+ import { handleNwcPay } from '../core/nwc-pay.js';
4
+ import { handleCashuRedeem } from '../core/cashu-redeem.js';
5
+ import { PAYMENT_HASH_RE } from '../core/types.js';
6
+ import { appendVary, applyNoStoreHeaders, stripProxyRequestHeaders, stripProxyResponseHeaders, } from './proxy-headers.js';
7
+ function setSensitiveHeaders(res, headers) {
8
+ const merged = headers ? new Headers(headers) : new Headers();
9
+ applyNoStoreHeaders(merged);
10
+ merged.forEach((value, key) => {
11
+ res.setHeader(key, value);
12
+ });
13
+ }
14
+ function jsonWithSensitiveHeaders(res, body, status = 200, headers) {
15
+ setSensitiveHeaders(res, headers);
16
+ res.status(status).json(body);
17
+ }
18
+ function htmlWithSensitiveHeaders(res, html, status = 200, headers) {
19
+ setSensitiveHeaders(res, headers);
20
+ res.status(status).type('html').send(html);
21
+ }
22
+ function buildUpstreamTarget(upstreamBase, originalUrl) {
23
+ const incoming = new URL(originalUrl, 'http://localhost');
24
+ const upstream = new URL(upstreamBase);
25
+ const upstreamPath = upstream.pathname.endsWith('/')
26
+ ? upstream.pathname.slice(0, -1)
27
+ : upstream.pathname;
28
+ const incomingPath = incoming.pathname.startsWith('/')
29
+ ? incoming.pathname
30
+ : `/${incoming.pathname}`;
31
+ upstream.pathname = `${upstreamPath}${incomingPath}` || '/';
32
+ upstream.search = incoming.search;
33
+ return upstream.href;
34
+ }
35
+ export function createExpressMiddleware(engineOrConfig, upstreamArg) {
36
+ // Support both old (engine, upstream) and new (config) signatures
37
+ const config = typeof upstreamArg === 'string'
38
+ ? { engine: engineOrConfig, upstream: upstreamArg }
39
+ : engineOrConfig;
40
+ const engine = config.engine;
41
+ // Warn when free-tier is enabled without trustProxy (P3) — all requests
42
+ // will share a single socket.remoteAddress behind a reverse proxy.
43
+ if (engine.freeTier && !config.trustProxy) {
44
+ console.error('[toll-booth] WARNING: freeTier enabled without trustProxy in Express adapter. ' +
45
+ 'Behind a reverse proxy all clients will share one IP bucket. ' +
46
+ 'Set trustProxy: true or provide a getClientIp callback.');
47
+ }
48
+ const upstreamBase = config.upstream.replace(/\/$/, '');
49
+ const extraHeaders = config.responseHeaders ?? {};
50
+ const upstreamTimeout = config.upstreamTimeout ?? 30_000;
51
+ return async (req, res, _next) => {
52
+ const ip = config.trustProxy
53
+ ? (typeof req.headers['x-forwarded-for'] === 'string'
54
+ ? req.headers['x-forwarded-for'].split(',')[0]?.trim()
55
+ : undefined) ??
56
+ (typeof req.headers['x-real-ip'] === 'string'
57
+ ? req.headers['x-real-ip'].trim()
58
+ : undefined) ??
59
+ req.socket.remoteAddress ??
60
+ '127.0.0.1'
61
+ : req.socket.remoteAddress ?? '127.0.0.1';
62
+ const headers = {};
63
+ for (const [key, value] of Object.entries(req.headers)) {
64
+ headers[key] = Array.isArray(value) ? value.join(', ') : value;
65
+ }
66
+ try {
67
+ const fullPath = (req.baseUrl + req.path).replace(/\/$/, '') || '/';
68
+ const result = await engine.handle({
69
+ method: req.method,
70
+ path: fullPath,
71
+ headers,
72
+ ip,
73
+ });
74
+ if (result.action === 'pass' || result.action === 'proxy') {
75
+ // Proxy to upstream
76
+ const target = buildUpstreamTarget(upstreamBase, req.originalUrl);
77
+ const incomingHeaders = new Headers();
78
+ for (const [key, value] of Object.entries(req.headers)) {
79
+ const v = Array.isArray(value) ? value.join(', ') : value;
80
+ if (v)
81
+ incomingHeaders.set(key, v);
82
+ }
83
+ const fwdHeaders = stripProxyRequestHeaders(incomingHeaders);
84
+ const init = {
85
+ method: req.method,
86
+ headers: fwdHeaders,
87
+ signal: AbortSignal.timeout(upstreamTimeout),
88
+ duplex: 'half',
89
+ };
90
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
91
+ // If body-parsing middleware already consumed the stream, re-serialise
92
+ if (req.body !== undefined && req.body !== null && typeof req.body === 'object' && Object.keys(req.body).length > 0) {
93
+ const json = JSON.stringify(req.body);
94
+ init.body = json;
95
+ fwdHeaders.set('content-length', Buffer.byteLength(json).toString());
96
+ }
97
+ else {
98
+ init.body = req;
99
+ }
100
+ }
101
+ const upstream_res = await fetch(target, init);
102
+ const responseHeaders = stripProxyResponseHeaders(upstream_res.headers);
103
+ responseHeaders.forEach((value, key) => {
104
+ res.setHeader(key, value);
105
+ });
106
+ // Set extra headers from engine result
107
+ for (const [key, value] of Object.entries(result.headers)) {
108
+ res.setHeader(key, value);
109
+ }
110
+ for (const [key, value] of Object.entries(extraHeaders)) {
111
+ res.setHeader(key, value);
112
+ }
113
+ const buf = Buffer.from(await upstream_res.arrayBuffer());
114
+ res.status(upstream_res.status).send(buf);
115
+ return;
116
+ }
117
+ // challenge -- 402
118
+ const challengeHeaders = new Headers();
119
+ for (const [key, value] of Object.entries(result.headers)) {
120
+ challengeHeaders.set(key, value);
121
+ }
122
+ for (const [key, value] of Object.entries(extraHeaders)) {
123
+ challengeHeaders.set(key, value);
124
+ }
125
+ jsonWithSensitiveHeaders(res, result.body, 402, challengeHeaders);
126
+ }
127
+ catch {
128
+ res.status(502).json({ error: 'Upstream routing engine unavailable' });
129
+ }
130
+ };
131
+ }
132
+ // -- Invoice status handler ---------------------------------------------------
133
+ /**
134
+ * Returns an Express `RequestHandler` that serves invoice status as JSON or HTML.
135
+ *
136
+ * Expects `:paymentHash` route param plus a `?token=...` status lookup secret.
137
+ * When `Accept: text/html` is requested, renders the self-service payment page;
138
+ * otherwise returns JSON with `{ paid, preimage }`.
139
+ */
140
+ export function createExpressInvoiceStatusHandler(deps) {
141
+ return async (req, res, _next) => {
142
+ const paymentHash = Array.isArray(req.params.paymentHash)
143
+ ? req.params.paymentHash[0]
144
+ : req.params.paymentHash;
145
+ if (!PAYMENT_HASH_RE.test(paymentHash)) {
146
+ res.status(400).json({ error: 'Invalid payment hash' });
147
+ return;
148
+ }
149
+ const statusToken = typeof req.query.token === 'string' ? req.query.token : undefined;
150
+ const accept = req.headers.accept ?? '';
151
+ try {
152
+ if (accept.includes('text/html')) {
153
+ const { html, status } = await renderInvoiceStatusHtml(deps, paymentHash, statusToken);
154
+ htmlWithSensitiveHeaders(res, html, status, appendVary(new Headers(), 'Accept'));
155
+ return;
156
+ }
157
+ const result = await handleInvoiceStatus(deps, paymentHash, statusToken);
158
+ if (!result.found) {
159
+ jsonWithSensitiveHeaders(res, { error: 'Invoice not found' }, 404, appendVary(new Headers(), 'Accept'));
160
+ return;
161
+ }
162
+ jsonWithSensitiveHeaders(res, { paid: result.paid, preimage: result.preimage, token_suffix: result.tokenSuffix }, 200, appendVary(new Headers(), 'Accept'));
163
+ }
164
+ catch {
165
+ jsonWithSensitiveHeaders(res, { error: 'Failed to check invoice status' }, 502, appendVary(new Headers(), 'Accept'));
166
+ }
167
+ };
168
+ }
169
+ // -- Create invoice handler ---------------------------------------------------
170
+ /**
171
+ * Returns an Express `RequestHandler` that creates a new Lightning invoice.
172
+ *
173
+ * Assumes `express.json()` middleware is already mounted. Delegates to the
174
+ * core `handleCreateInvoice` and returns the result.
175
+ */
176
+ export function createExpressCreateInvoiceHandler(deps) {
177
+ return async (req, res, _next) => {
178
+ const body = req.body ?? {};
179
+ const result = await handleCreateInvoice(deps, body);
180
+ if (!result.success) {
181
+ jsonWithSensitiveHeaders(res, { error: result.error, tiers: result.tiers }, 400);
182
+ return;
183
+ }
184
+ const d = result.data;
185
+ jsonWithSensitiveHeaders(res, {
186
+ bolt11: d.bolt11,
187
+ payment_hash: d.paymentHash,
188
+ payment_url: d.paymentUrl,
189
+ amount_sats: d.amountSats,
190
+ credit_sats: d.creditSats,
191
+ macaroon: d.macaroon,
192
+ qr_svg: d.qrSvg,
193
+ });
194
+ };
195
+ }
196
+ // -- NWC handler --------------------------------------------------------------
197
+ /**
198
+ * Returns an Express `RequestHandler` that pays a Lightning invoice via NWC.
199
+ *
200
+ * Expects JSON body with `{ nwcUri, bolt11, paymentHash, statusToken }`.
201
+ * Returns the payment preimage on success.
202
+ */
203
+ export function createExpressNwcHandler(deps) {
204
+ return async (req, res, _next) => {
205
+ const body = req.body ?? {};
206
+ const result = await handleNwcPay(deps, body);
207
+ if (result.success) {
208
+ jsonWithSensitiveHeaders(res, { preimage: result.preimage });
209
+ }
210
+ else {
211
+ jsonWithSensitiveHeaders(res, { error: result.error }, result.status);
212
+ }
213
+ };
214
+ }
215
+ // -- Cashu handler ------------------------------------------------------------
216
+ /**
217
+ * Returns an Express `RequestHandler` that redeems a Cashu token as payment.
218
+ *
219
+ * Expects JSON body with `{ token, paymentHash, statusToken }`.
220
+ * Uses durable claims and leases to avoid concurrent duplicate redemption.
221
+ */
222
+ export function createExpressCashuHandler(deps) {
223
+ return async (req, res, _next) => {
224
+ const body = req.body ?? {};
225
+ const result = await handleCashuRedeem(deps, body);
226
+ if (result.success) {
227
+ jsonWithSensitiveHeaders(res, { credited: result.credited, token_suffix: result.tokenSuffix });
228
+ }
229
+ else if ('state' in result) {
230
+ jsonWithSensitiveHeaders(res, { state: result.state, retryAfterMs: result.retryAfterMs }, 202);
231
+ }
232
+ else {
233
+ jsonWithSensitiveHeaders(res, { error: result.error }, result.status);
234
+ }
235
+ };
236
+ }
237
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ type HeaderSource = Headers | Record<string, string> | Array<[string, string]>;
2
+ export declare function stripProxyRequestHeaders(source: HeaderSource): Headers;
3
+ export declare function stripProxyResponseHeaders(source: HeaderSource): Headers;
4
+ export declare function applyNoStoreHeaders(headers: Headers): Headers;
5
+ export declare function appendVary(headers: Headers, value: string): Headers;
6
+ export {};
7
+ //# sourceMappingURL=proxy-headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-headers.d.ts","sourceRoot":"","sources":["../../src/adapters/proxy-headers.ts"],"names":[],"mappings":"AAYA,KAAK,YAAY,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAE9E,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAWtE;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CASvE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAI7D;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAWnE"}
@@ -0,0 +1,58 @@
1
+ const BASE_HOP_BY_HOP_HEADERS = new Set([
2
+ 'connection',
3
+ 'content-length',
4
+ 'keep-alive',
5
+ 'proxy-authenticate',
6
+ 'proxy-authorization',
7
+ 'te',
8
+ 'trailer',
9
+ 'transfer-encoding',
10
+ 'upgrade',
11
+ ]);
12
+ export function stripProxyRequestHeaders(source) {
13
+ const headers = new Headers(source);
14
+ const disallowed = collectDisallowedHeaders(headers);
15
+ disallowed.add('authorization');
16
+ disallowed.add('host');
17
+ for (const name of disallowed) {
18
+ headers.delete(name);
19
+ }
20
+ return headers;
21
+ }
22
+ export function stripProxyResponseHeaders(source) {
23
+ const headers = new Headers(source);
24
+ const disallowed = collectDisallowedHeaders(headers);
25
+ for (const name of disallowed) {
26
+ headers.delete(name);
27
+ }
28
+ return headers;
29
+ }
30
+ export function applyNoStoreHeaders(headers) {
31
+ headers.set('Cache-Control', 'no-store');
32
+ headers.set('Pragma', 'no-cache');
33
+ return headers;
34
+ }
35
+ export function appendVary(headers, value) {
36
+ const current = headers.get('Vary');
37
+ if (!current) {
38
+ headers.set('Vary', value);
39
+ return headers;
40
+ }
41
+ const values = new Set(current.split(',').map(v => v.trim()).filter(Boolean));
42
+ values.add(value);
43
+ headers.set('Vary', Array.from(values).join(', '));
44
+ return headers;
45
+ }
46
+ function collectDisallowedHeaders(headers) {
47
+ const disallowed = new Set(BASE_HOP_BY_HOP_HEADERS);
48
+ const connection = headers.get('connection');
49
+ if (connection) {
50
+ for (const token of connection.split(',')) {
51
+ const name = token.trim().toLowerCase();
52
+ if (name)
53
+ disallowed.add(name);
54
+ }
55
+ }
56
+ return disallowed;
57
+ }
58
+ //# sourceMappingURL=proxy-headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-headers.js","sourceRoot":"","sources":["../../src/adapters/proxy-headers.ts"],"names":[],"mappings":"AAAA,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC;IACtC,YAAY;IACZ,gBAAgB;IAChB,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,IAAI;IACJ,SAAS;IACT,mBAAmB;IACnB,SAAS;CACV,CAAC,CAAA;AAIF,MAAM,UAAU,wBAAwB,CAAC,MAAoB;IAC3D,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IACnC,MAAM,UAAU,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAA;IACpD,UAAU,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IAC/B,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAEtB,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACtB,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,MAAoB;IAC5D,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;IACnC,MAAM,UAAU,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAA;IAEpD,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACtB,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;IACjC,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAgB,EAAE,KAAa;IACxD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACnC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QAC1B,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;IAC7E,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACjB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAClD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,SAAS,wBAAwB,CAAC,OAAgB;IAChD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,uBAAuB,CAAC,CAAA;IACnD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IAE5C,IAAI,UAAU,EAAE,CAAC;QACf,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;YACvC,IAAI,IAAI;gBAAE,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAChC,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC"}