|
| 1 | +# l402proxy |
| 2 | + |
| 3 | +Drop an L402 payment gate in front of any HTTP service in one command. |
| 4 | + |
| 5 | +No database, no ceremony — single binary + Lightning node. |
| 6 | + |
| 7 | +## How it works |
| 8 | + |
| 9 | +1. Request arrives with no auth → proxy responds `402 Payment Required` with a BOLT-11 invoice and a token in `WWW-Authenticate` and the JSON body |
| 10 | +2. Client pays the invoice, receives the preimage |
| 11 | +3. Client retries with `Authorization: L402 <token>:<preimage>` → proxy verifies payment → forwards to upstream |
| 12 | + |
| 13 | +## Installation |
| 14 | + |
| 15 | +```sh |
| 16 | +go install github.com/qustavo/l402proxy/cmd/l402proxy@latest |
| 17 | +``` |
| 18 | + |
| 19 | +## Usage |
| 20 | + |
| 21 | +```sh |
| 22 | +l402proxy \ |
| 23 | + --upstream http://localhost:3000 \ |
| 24 | + --price 10sat \ |
| 25 | + --lnd-host localhost:10009 \ |
| 26 | + --lnd-macaroon ~/.lnd/data/chain/bitcoin/mainnet/admin.macaroon \ |
| 27 | + --lnd-cert ~/.lnd/tls.cert \ |
| 28 | + --secret-key $(openssl rand -hex 32) |
| 29 | +``` |
| 30 | + |
| 31 | +### Flags |
| 32 | + |
| 33 | +| Flag | Default | Description | |
| 34 | +|---|---|---| |
| 35 | +| `--upstream` | required | URL of the backend service | |
| 36 | +| `--price` | required | Price per request (`10sat`, `1000msat`) | |
| 37 | +| `--listen` | `:8080` | Address to listen on | |
| 38 | +| `--lnd-host` | `localhost:10009` | LND gRPC host:port | |
| 39 | +| `--lnd-macaroon` | `~/.lnd/data/chain/bitcoin/mainnet/admin.macaroon` | Path to LND admin macaroon | |
| 40 | +| `--lnd-cert` | `~/.lnd/tls.cert` | Path to LND TLS cert | |
| 41 | +| `--service-name` | `l402proxy` | Label used in invoice memos | |
| 42 | +| `--secret-key` | auto-generated | Hex-encoded 32-byte HMAC secret (tokens won't survive restarts if omitted) | |
| 43 | + |
| 44 | +## L402 flow (curl example) |
| 45 | + |
| 46 | +**Step 1 — first request, no auth:** |
| 47 | + |
| 48 | +```sh |
| 49 | +$ curl -i http://localhost:8080/api/data |
| 50 | + |
| 51 | +HTTP/1.1 402 Payment Required |
| 52 | +WWW-Authenticate: L402 token="eyJ...", invoice="lnbc..." |
| 53 | + |
| 54 | +{"token":"eyJ...","invoice":"lnbc...","amount_msat":10000} |
| 55 | +``` |
| 56 | + |
| 57 | +**Step 2 — pay the invoice, get the preimage:** |
| 58 | + |
| 59 | +```sh |
| 60 | +$ lncli payinvoice lnbc... |
| 61 | +# → preimage: aabbccdd... |
| 62 | +``` |
| 63 | + |
| 64 | +**Step 3 — retry with credentials:** |
| 65 | + |
| 66 | +```sh |
| 67 | +$ curl -H "Authorization: L402 eyJ...:aabbccdd..." http://localhost:8080/api/data |
| 68 | + |
| 69 | +HTTP/1.1 200 OK |
| 70 | +... |
| 71 | +``` |
| 72 | + |
| 73 | +## Token format |
| 74 | + |
| 75 | +`base64url(json_payload).hex(hmac_sha256)` — stateless, no database required. Token TTL is 24 hours. |
| 76 | + |
| 77 | +## Comparison with Aperture |
| 78 | + |
| 79 | +[Aperture](https://github.com/lightninglabs/aperture) is the reference L402 proxy by Lightning Labs. |
| 80 | + |
| 81 | +| | Aperture | l402proxy | |
| 82 | +|---|---|---| |
| 83 | +| Database | etcd / Postgres / SQLite | None — stateless | |
| 84 | +| Config | Complex YAML | CLI flags | |
| 85 | +| LN backends | LND only | Pluggable interface | |
| 86 | +| Deployment | Multi-service setup | Single binary | |
| 87 | +| Use as library | No | Yes (Go middleware) | |
| 88 | +| Target audience | Production infra teams | Any developer, AI builders | |
| 89 | + |
| 90 | +l402proxy is the zero-friction entry point — the thing you use to get started in 5 minutes. |
0 commit comments