{"id":47610591,"url":"https://github.com/ambosstech/lightning-mpp-sdk","last_synced_at":"2026-04-01T20:02:06.473Z","repository":{"id":345392901,"uuid":"1185584633","full_name":"AmbossTech/lightning-mpp-sdk","owner":"AmbossTech","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-18T22:59:18.000Z","size":108,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-19T11:42:41.457Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AmbossTech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-18T18:29:51.000Z","updated_at":"2026-03-18T22:59:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AmbossTech/lightning-mpp-sdk","commit_stats":null,"previous_names":["ambosstech/lightning-mpp-sdk"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/AmbossTech/lightning-mpp-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmbossTech%2Flightning-mpp-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmbossTech%2Flightning-mpp-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmbossTech%2Flightning-mpp-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmbossTech%2Flightning-mpp-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AmbossTech","download_url":"https://codeload.github.com/AmbossTech/lightning-mpp-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AmbossTech%2Flightning-mpp-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291337,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-01T20:01:17.428Z","updated_at":"2026-04-01T20:02:06.429Z","avatar_url":"https://github.com/AmbossTech.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lightning MPP SDK\n\nA provider-agnostic Lightning Network payment method for [MPP](https://mpp.dev) (Machine Payments Protocol).\n\n\u003e **Note for Lightning developers:** \"MPP\" here refers to **Machine Payments Protocol**, an open HTTP payment standard — not [Multi-Path Payments](https://bitcoinops.org/en/topics/multipath-payments/) (BOLT #4).\n\nMPP lets any HTTP API accept payments using the standard `402 Payment Required` flow. This SDK implements the Lightning payment method with a pluggable provider architecture — bring your own LND node, or swap in any Lightning implementation.\n\n## How it works\n\n```\nClient                          Server\n  │                                 │\n  │────── GET /resource ───────────\u003e│\n  │                                 │\n  │\u003c──── 402 + BOLT11 invoice ──────│  server generates invoice via provider\n  │                                 │\n  │   (pay invoice over Lightning)  │\n  │                                 │\n  │─── GET /resource + preimage ───\u003e│\n  │                                 │  server verifies sha256(preimage) == paymentHash\n  │\u003c────── 200 + resource ──────────│\n  │                                 │\n```\n\nNo external payment processor. No polling. No webhooks. The preimage _is_ the proof of payment.\n\n## Packages\n\n| Package                                  | Description                                                      |\n| ---------------------------------------- | ---------------------------------------------------------------- |\n| `@ambosstech/lightning-mpp-sdk`         | Core SDK — method definitions, provider interface, store, errors |\n| `@ambosstech/lightning-mpp-adapter-lnd`  | LND adapter — gRPC and REST transports                           |\n| `@ambosstech/lightning-mpp-adapter-nwc`  | NWC adapter — Nostr Wallet Connect (NIP-47) over relays         |\n| `@ambosstech/lightning-mpp-adapter-mock` | Mock adapter — for testing without a real Lightning node         |\n\n## Quick start\n\n### Installation\n\n```bash\n# Core + LND adapter\npnpm add @ambosstech/lightning-mpp-sdk @ambosstech/lightning-mpp-adapter-lnd mppx\n\n# Or core + NWC adapter (Alby Hub, coinos, Primal, etc.)\npnpm add @ambosstech/lightning-mpp-sdk @ambosstech/lightning-mpp-adapter-nwc mppx\n\n# Or core + mock for testing\npnpm add @ambosstech/lightning-mpp-sdk @ambosstech/lightning-mpp-adapter-mock mppx\n```\n\n### Server — charge (one-time payment)\n\nUses the Web-standard `Request`/`Response` API — works with Node.js, Cloudflare Workers, Next.js, Bun, Deno, and any other runtime.\n\n```ts\nimport { Mppx } from \"mppx\";\nimport {\n  lightningChargeServer,\n  createMemoryStore,\n} from \"@ambosstech/lightning-mpp-sdk\";\nimport { LndLightningProvider } from \"@ambosstech/lightning-mpp-adapter-lnd\";\n\n// 1. Create a provider (LND in this example)\nconst provider = new LndLightningProvider({\n  transport: \"rest\",\n  url: \"https://127.0.0.1:8080\",\n  macaroon: process.env.LND_MACAROON!,\n});\n\n// 2. Create the server-side charge method\nconst chargeMethod = lightningChargeServer({\n  provider,\n  currency: \"sat\",\n  network: \"mainnet\",\n});\n\n// 3. Wire it into mppx\nconst mppx = Mppx.create({\n  methods: [chargeMethod],\n  secretKey: process.env.MPP_SECRET_KEY!,\n});\n\n// 4. Use in your request handler\nexport async function handler(request: Request): Promise\u003cResponse\u003e {\n  const result = await mppx.charge({\n    amount: \"100\",\n    currency: \"sat\",\n    description: \"Premium API access\",\n  })(request);\n\n  if (result.status === 402) return result.challenge;\n\n  return result.withReceipt(Response.json({ data: \"...\" }));\n}\n```\n\n### Client — charge (one-time payment)\n\nThe MPP client intercepts `402` responses automatically — paying invoices and retrying with credentials before returning the final response.\n\n```ts\nimport { Mppx } from \"mppx\";\nimport { lightningChargeClient } from \"@ambosstech/lightning-mpp-sdk\";\nimport { LndLightningProvider } from \"@ambosstech/lightning-mpp-adapter-lnd\";\n\nconst provider = new LndLightningProvider({\n  transport: \"rest\",\n  url: \"https://127.0.0.1:8080\",\n  macaroon: process.env.LND_MACAROON!,\n});\n\nconst chargeClient = lightningChargeClient(provider, {\n  maxFeeSats: 100,\n  onProgress: (event) =\u003e console.log(event.type),\n});\n\nconst mppx = Mppx.create({\n  polyfill: false,\n  methods: [chargeClient],\n});\n\nconst response = await mppx.fetch(\"https://api.example.com/weather\");\nconsole.log(await response.json());\n```\n\n### Server — session (prepaid metered access)\n\nSessions let clients deposit a lump sum and make multiple requests, with per-request billing deducted from the balance. Supports mid-stream top-ups and refunds on close.\n\n```ts\nimport { Mppx } from \"mppx\";\nimport {\n  lightningSessionServer,\n  createMemoryStore,\n} from \"@ambosstech/lightning-mpp-sdk\";\nimport { LndLightningProvider } from \"@ambosstech/lightning-mpp-adapter-lnd\";\n\nconst provider = new LndLightningProvider({\n  transport: \"rest\",\n  url: \"https://127.0.0.1:8080\",\n  macaroon: process.env.LND_MACAROON!,\n});\n\nconst sessionMethod = lightningSessionServer({\n  provider,\n  depositAmount: 300, // sats required upfront\n  idleTimeout: 300, // auto-close after 5 min inactivity\n  unitType: \"chunk\",\n});\n\nconst mppx = Mppx.create({\n  methods: [sessionMethod],\n  secretKey: process.env.MPP_SECRET_KEY!,\n});\n\nexport async function handler(request: Request): Promise\u003cResponse\u003e {\n  const result = await mppx.session({\n    amount: \"2\", // 2 sats per chunk\n    currency: \"sat\",\n    description: \"LLM stream\",\n  })(request);\n\n  if (result.status === 402) return result.challenge;\n\n  // Use the built-in serve() for automatic per-chunk billing over SSE\n  return result.withReceipt(\n    sessionMethod.serve({\n      sessionId: \"...\", // from the credential\n      satsPerChunk: 2,\n      generate: myAsyncGenerator(),\n      timeoutMs: 60_000, // wait 60s for top-up before closing\n    }),\n  );\n}\n```\n\nThe `serve()` method handles the full SSE lifecycle:\n\n- Deducts `satsPerChunk` from the session balance for each yielded value\n- Emits `payment-need-topup` when balance is exhausted\n- Holds the connection open until the client tops up or timeout elapses\n- Emits `payment-receipt` and `[DONE]` on stream completion\n\n### Client — session\n\n```ts\nimport { Mppx } from \"mppx\";\nimport { lightningSessionClient } from \"@ambosstech/lightning-mpp-sdk\";\nimport { LndLightningProvider } from \"@ambosstech/lightning-mpp-adapter-lnd\";\n\nconst provider = new LndLightningProvider({\n  transport: \"rest\",\n  url: \"https://127.0.0.1:8080\",\n  macaroon: process.env.LND_MACAROON!,\n});\n\nconst sessionClient = lightningSessionClient(provider, {\n  maxFeeSats: 100,\n  onProgress: (event) =\u003e console.log(event.type),\n});\n\nconst mppx = Mppx.create({\n  polyfill: false,\n  methods: [sessionClient],\n});\n\n// First request opens the session and pays the deposit\nconst response1 = await mppx.fetch(\"https://api.example.com/generate\");\n\n// Subsequent requests reuse the session (bearer auth, no payment)\nconst response2 = await mppx.fetch(\"https://api.example.com/generate\");\n\n// Top up if balance is exhausted mid-stream\nawait sessionClient.topUp(mppx.fetch, \"https://api.example.com/generate\");\n\n// Close the session and get a refund for unspent balance\nconst closeResponse = await sessionClient.close(\n  mppx.fetch,\n  \"https://api.example.com/generate\",\n);\nconst { refundSats } = await closeResponse.json();\n```\n\n## Testing with the mock adapter\n\nThe mock adapter lets you test the full payment flow without a real Lightning node:\n\n```ts\nimport {\n  lightningChargeServer,\n  lightningChargeClient,\n  createMemoryStore,\n} from \"@ambosstech/lightning-mpp-sdk\";\nimport { MockLightningProvider } from \"@ambosstech/lightning-mpp-adapter-mock\";\n\n// Server-side mock (auto-settles invoices)\nconst serverProvider = new MockLightningProvider({ autoSettle: true });\nconst server = lightningChargeServer({ provider: serverProvider });\n\n// Client-side mock (pays invoices instantly)\nconst clientProvider = new MockLightningProvider();\nconst client = lightningChargeClient(clientProvider);\n\n// Test failure scenarios\nconst failProvider = new MockLightningProvider({ failOnPay: true });\nconst failClient = lightningChargeClient(failProvider);\n\n// Simulate slow payments\nconst slowProvider = new MockLightningProvider({ paymentDelay: 2000 });\n```\n\n### Mock provider options\n\n| Option         | Type      | Default | Description                                    |\n| -------------- | --------- | ------- | ---------------------------------------------- |\n| `autoSettle`   | `boolean` | `true`  | Auto-mark invoices as settled on lookup        |\n| `failOnPay`    | `boolean` | `false` | Throw `RouteNotFoundError` on payment          |\n| `paymentDelay` | `number`  | `0`     | Artificial delay in ms before payment resolves |\n\n## LND adapter configuration\n\n### gRPC transport\n\n```ts\nimport { LndLightningProvider } from \"@ambosstech/lightning-mpp-adapter-lnd\";\nimport { readFileSync } from \"node:fs\";\n\nconst provider = new LndLightningProvider({\n  transport: \"grpc\",\n  host: \"127.0.0.1:10009\",\n  tlsCert: readFileSync(\"/path/to/tls.cert\"),\n  macaroon: readFileSync(\"/path/to/admin.macaroon\"),\n});\n```\n\n### REST transport\n\n```ts\nconst provider = new LndLightningProvider({\n  transport: \"rest\",\n  url: \"https://127.0.0.1:8080\",\n  macaroon: process.env.LND_MACAROON!, // hex-encoded\n  fetch: customFetchWithTLS, // optional: custom fetch for TLS cert handling\n});\n```\n\n## NWC adapter configuration\n\nConnect to any [Nostr Wallet Connect](https://github.com/nostr-protocol/nips/blob/master/47.md) (NIP-47) compatible wallet using a connection string:\n\n```ts\nimport { NwcLightningProvider } from \"@ambosstech/lightning-mpp-adapter-nwc\";\n\nconst provider = new NwcLightningProvider({\n  connectionString: \"nostr+walletconnect://pubkey?relay=wss://relay.example.com\u0026secret=hex\",\n  timeoutSecs: 60, // optional, default 60\n});\n\n// Use like any other provider\nconst invoice = await provider.createInvoice({ amountSats: 1000, memo: \"test\" });\n\n// Clean up when done\nprovider.close();\n```\n\nThe connection string is typically provided by your wallet (Alby Hub, coinos, Primal, etc.). It contains the wallet pubkey, relay URL, and client secret. All communication is encrypted with NIP-44.\n\n\u003e **Note:** NWC does not support `maxFeeSats` — fee limits are controlled by the wallet. If provided, it will be ignored with a warning.\n\n## Provider interface\n\nImplement `LightningProvider` to add support for any Lightning node or wallet:\n\n```ts\nimport type { LightningProvider } from \"@ambosstech/lightning-mpp-sdk\";\n\nclass MyCustomProvider implements LightningProvider {\n  async createInvoice(params: {\n    amountSats: number;\n    memo?: string;\n    expirySecs?: number;\n  }) {\n    // Return { bolt11: string, paymentHash: string }\n  }\n\n  async payInvoice(params: {\n    bolt11: string;\n    amountSats?: number; // required for 0-amount invoices (e.g. session refunds)\n    maxFeeSats?: number;\n    timeoutSecs?: number;\n  }) {\n    // Return { preimage: string }\n  }\n\n  async lookupInvoice(params: { paymentHash: string }) {\n    // Return { settled: boolean, preimage?: string, amountSats?: number }\n  }\n}\n```\n\n## Pluggable store\n\nSession state and consume-once tracking use a `KeyValueStore` interface. The default is in-memory — swap it for Redis, Cloudflare KV, DynamoDB, etc. in production:\n\n```ts\nimport type { KeyValueStore } from \"@ambosstech/lightning-mpp-sdk\";\n\nconst redisStore: KeyValueStore = {\n  async get\u003cT\u003e(key: string): Promise\u003cT | undefined\u003e {\n    const value = await redis.get(key);\n    return value ? JSON.parse(value) : undefined;\n  },\n  async put\u003cT\u003e(key: string, value: T): Promise\u003cvoid\u003e {\n    await redis.set(key, JSON.stringify(value));\n  },\n};\n\nconst server = lightningChargeServer({ provider, store: redisStore });\n```\n\n## Error handling\n\nThe SDK provides typed error classes for common Lightning failure modes:\n\n```ts\nimport {\n  LightningError,\n  InsufficientBalanceError,\n  InvoiceExpiredError,\n  RouteNotFoundError,\n  PaymentTimeoutError,\n  ConnectionError,\n  AuthenticationError,\n} from \"@ambosstech/lightning-mpp-sdk\";\n\ntry {\n  await provider.payInvoice({ bolt11: invoice });\n} catch (error) {\n  if (error instanceof RouteNotFoundError) {\n    // No path to destination — retry later or use a different route\n  } else if (error instanceof PaymentTimeoutError) {\n    // Payment did not complete in time\n  } else if (error instanceof InsufficientBalanceError) {\n    // Not enough local balance to send\n  }\n}\n```\n\n| Error                      | Code                   | When                             |\n| -------------------------- | ---------------------- | -------------------------------- |\n| `InsufficientBalanceError` | `INSUFFICIENT_BALANCE` | Not enough local balance         |\n| `InvoiceExpiredError`      | `INVOICE_EXPIRED`      | Invoice TTL has elapsed          |\n| `RouteNotFoundError`       | `ROUTE_NOT_FOUND`      | No route to destination          |\n| `PaymentTimeoutError`      | `PAYMENT_TIMEOUT`      | Payment did not complete in time |\n| `ConnectionError`          | `CONNECTION_ERROR`     | Cannot reach the Lightning node  |\n| `AuthenticationError`      | `AUTHENTICATION_ERROR` | Invalid macaroon or credentials  |\n\n## API reference\n\n### Charge method\n\n#### `lightningChargeServer(options)`\n\n| Option              | Type                | Default      | Description                                        |\n| ------------------- | ------------------- | ------------ | -------------------------------------------------- |\n| `provider`          | `LightningProvider` | **required** | Lightning node adapter                             |\n| `store`             | `KeyValueStore`     | in-memory    | For consume-once tracking                          |\n| `currency`          | `string`            | `\"sat\"`      | Currency code sent in challenges                   |\n| `network`           | `string`            | —            | Network name sent in challenges (e.g. `\"mainnet\"`) |\n| `invoiceExpirySecs` | `number`            | `3600`       | Invoice TTL in seconds                             |\n\n#### `lightningChargeClient(provider, options?)`\n\n| Option       | Type              | Default | Description                                       |\n| ------------ | ----------------- | ------- | ------------------------------------------------- |\n| `maxFeeSats` | `number`          | —       | Maximum routing fee                               |\n| `onProgress` | `(event) =\u003e void` | —       | Progress callback (`challenge`, `paying`, `paid`) |\n\n### Session method\n\n#### `lightningSessionServer(options)`\n\n| Option          | Type                | Default       | Description                                |\n| --------------- | ------------------- | ------------- | ------------------------------------------ |\n| `provider`      | `LightningProvider` | **required**  | Lightning node adapter                     |\n| `store`         | `KeyValueStore`     | in-memory     | For session state                          |\n| `currency`      | `string`            | `\"sat\"`       | Currency code                              |\n| `depositAmount` | `number`            | `amount * 20` | Deposit size in sats                       |\n| `unitType`      | `string`            | —             | Label for the priced unit (e.g. `\"token\"`) |\n| `idleTimeout`   | `number`            | `300`         | Idle timeout in seconds (0 to disable)     |\n\nReturns the method plus `{ deduct, waitForTopUp, serve }`:\n\n- **`deduct(sessionId, sats)`** — Deducts from balance. Returns `true` on success, `false` if insufficient.\n- **`waitForTopUp(sessionId, timeoutMs?)`** — Waits for a top-up. Returns `true` if topped up, `false` on timeout.\n- **`serve({ sessionId, satsPerChunk, generate, timeoutMs? })`** — Returns an SSE `Response` with automatic per-chunk billing.\n\n#### `lightningSessionClient(provider, options?)`\n\n| Option       | Type              | Default | Description                                                        |\n| ------------ | ----------------- | ------- | ------------------------------------------------------------------ |\n| `maxFeeSats` | `number`          | —       | Maximum routing fee                                                |\n| `onProgress` | `(event) =\u003e void` | —       | Progress callback (`opening`, `bearer`, `topping-up`, `topped-up`) |\n\nReturns the method plus `{ close, topUp, getSession, resetSession }`:\n\n- **`topUp(fetch, url)`** — Pays a new deposit invoice to add balance.\n- **`close(fetch, url)`** — Closes the session and triggers a refund.\n- **`getSession()`** — Returns `{ sessionId }` or `null`.\n- **`resetSession()`** — Clears local state (for server-initiated closes).\n\n## Development\n\n### Prerequisites\n\n- Node.js 22+\n- pnpm 9+\n\n### Setup\n\n```bash\ngit clone https://github.com/ambosstech/lightning-mpp-sdk.git\ncd lightning-mpp-sdk\npnpm install\n```\n\n### Commands\n\n```bash\npnpm build          # Build all packages\npnpm test           # Run all tests\npnpm test:watch     # Run tests in watch mode\npnpm typecheck      # TypeScript type checking\npnpm lint           # ESLint\npnpm format         # Prettier\n```\n\n### Project structure\n\n```\nlightning-mpp-sdk/\n├── packages/\n│   ├── sdk/                # Core SDK — methods, provider interface, store, errors\n│   │   └── src/\n│   │       ├── methods/     # charge.ts, session.ts (client + server)\n│   │       ├── session/     # SessionStateManager (low-level API)\n│   │       ├── provider.ts  # LightningProvider interface\n│   │       ├── store.ts     # KeyValueStore interface\n│   │       ├── errors.ts    # Typed error classes\n│   │       └── preimage.ts  # Crypto utilities (sha256 verification, hex/base64)\n│   ├── adapter-lnd/         # LND adapter (gRPC + REST)\n│   ├── adapter-nwc/         # NWC adapter (Nostr Wallet Connect)\n│   └── adapter-mock/        # Mock adapter for testing\n├── package.json             # Workspace root\n├── pnpm-workspace.yaml\n├── turbo.json               # Build orchestration\n└── tsconfig.base.json       # Shared TypeScript config\n```\n\n## Specifications\n\nThe Lightning payment method is defined in two IETF-formatted specifications within the [HTTP Payment Authentication](https://paymentauth.org) framework:\n\n- [`draft-lightning-charge-00`](https://paymentauth.org/draft-lightning-charge-00.html) — One-time BOLT11 invoice payments\n- [`draft-lightning-session-00`](https://paymentauth.org/draft-lightning-session-00.html) — Prepaid sessions with per-unit billing and refund on close\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fambosstech%2Flightning-mpp-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fambosstech%2Flightning-mpp-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fambosstech%2Flightning-mpp-sdk/lists"}