ngx-l402

Accept Lightning payments in front of any HTTP API.

ngx-l402 is an open-source nginx module that puts a Lightning paywall in front of any upstream — no API rewrite, no SDK. Speaks L402 (bLIP-26) today and MPP-Lightning (IETF draft-ryan-httpauth-payment) next, both verifying the same sha256(preimage) primitive on the same gateway.

/etc/nginx/conf.d/api.conf
# Put L402 in front of any upstream — no API rewrite.
server {
  listen 443 ssl;
  server_name api.example.com;

  location /v1/ {
    l402             on;
    l402_price       1000;          # sats per request
    l402_backend     lnd;
    l402_lnd_host    https://lnd.internal:8080;
    l402_macaroon    /etc/lnd/invoice.macaroon;

    proxy_pass       http://upstream;
  }
}

↑ that block turns api.example.com/v1/ into a 1,000-sat-per-request Lightning endpoint. No application code touched.

How it works, in three requests.

The same primitive — sha256(preimage) == paymentHash — drives both L402 and MPP-Lightning. The client (a human, a script, an AI agent) hits the endpoint, pays the returned invoice on Lightning, and retries with the preimage. No accounts, no API keys, no chain confirmations.

$ first request 01 · challenge
# Hit the protected endpoint with no auth.
$ curl -i https://api.example.com/v1/chat

HTTP/1.1 402 Payment Required
WWW-Authenticate: L402 macaroon="AGIAJEemVQUTEyNCR0exk7ek...",
                  invoice="lnbc10u1p3xnhl2pp5..."
Content-Type: application/json
Cache-Control: no-store

{
  "amount_sat": 1000,
  "description": "chat.completions · 1 request",
  "expires_at": "2026-05-21T18:04:11Z"
}
$ pay invoice 02 · settle
# Agent pays the invoice on its Lightning node.
# Any of 8 backends works — LND shown here.
$ lncli payinvoice \
  --pay_req=lnbc10u1p3xnhl2pp5...

Payment hash: 9c3a2b1f8e7d6c5b4a39281706f5e4d3...
Payment preimage: 7a3c91b4e2d058...
Status: SUCCEEDED  ·  Fee: 0 sat  ·  Hops: 3

# sha256(preimage) == paymentHash → proof of payment.
# Same primitive serves L402 and MPP-Lightning.
$ retry with preimage 03 · 200 OK
# Repeat the request with macaroon + preimage.
$ curl -H "Authorization: L402 \
    $MACAROON:$PREIMAGE" \
  https://api.example.com/v1/chat

HTTP/1.1 200 OK
Content-Type: application/json
X-L402-Settled: "1000 sat"

{
  "reply": "hello, agent.",
  "model": "gpt-4o-mini",
  "tokens": 42
}
// settlement: median 280 ms, p99 1.4 s · no chain confirmations

Three reasons it lives at the edge.

01 · drop-in

One nginx directive, no API rewrite.

Two lines in your existing nginx config wrap any upstream — no SDK to import, no application code to touch, no language lock-in. The diff fits in one commit, and reviews like any other reverse-proxy change.

02 · multi-backend

Eight Lightning backends, one config line.

Point ngx-l402 at the Lightning infrastructure you already run. Swap backends without touching your application — change one directive and reload nginx.

LND
CLN
Eclair
LNC
LNURL
NWC
BOLT12
Cashubeta
03 · two protocols

L402 today, MPP-Lightning next.

Speaks L402 (Lightning Labs bLIP-26) today. The upcoming release adds MPP-Lightning, the Lightning method of the IETF Machine Payments Protocol — both verify the same primitive, so the same Lightning backend serves both.

L402 bLIP-26 · live MPP-Lightning draft-ryan-httpauth-payment · next

Run the gateway in 60 seconds.

Pre-built container, one docker command, no dependencies beyond a Lightning node you can reach. (For local testing, point it at a regtest LND or a Cashu mint.)

$ run the gateway
$ docker run --rm -p 8080:80 \
  -v $(pwd)/nginx.conf:/etc/nginx/conf.d/api.conf \
  -v $(pwd)/invoice.macaroon:/etc/lnd/invoice.macaroon:ro \
  ghcr.io/ngx-l402/ngx-l402:1.2.5

# Gateway listens on :8080. Point your client at it.
# Mount your own nginx.conf + Lightning credentials.
  1. 01

    Configure & run

    Drop a nginx.conf with one l402-enabled location into the container. The image bundles nginx, the module, and clients for all 8 backends.

  2. 02

    Hit the endpoint

    Any request to a protected route comes back as 402 Payment Required with a Lightning invoice in the WWW-Authenticate header.

  3. 03

    Pay → preimage → 200

    Pay the invoice from any Lightning wallet. Retry with the preimage in the Authorization header — the gateway verifies locally and proxies through to your upstream.