L402 (Lightning HTTP 402)
July 9, 2026 ยท View on GitHub
Last modified: 2026-07-09
L402 support is a design direction, and none of it ships in the current binary. There is no macaroon primitive, no L402 issuer or verifier, and no invoice-provider seam anywhere in the codebase today. This page records the protocol shape SBproxy would implement so the wire format is agreed before any code lands. For the 402 payment surface that does ship, see 402-challenge.md and ai-crawl-control.md.
Why L402
The Lightning Labs L402 protocol pairs an HTTP 402 challenge with a macaroon bearer credential and a Lightning invoice. A proxy that speaks it can sit in front of paywalled HTTP resources as a drop-in alternative to aperture. The rest of this page explains the protocol; treat every "would" below literally.
Wire shape
Initial challenge:
HTTP/1.1 402 Payment Required
WWW-Authenticate: L402 macaroon="<base64url>", invoice="lnbc..."
Client retry after paying the invoice and receiving the preimage:
GET /resource HTTP/1.1
Authorization: L402 <macaroon>:<preimage_hex>
A verifier would:
- Parse the
Authorizationheader into(macaroon, preimage). - Recompute the macaroon HMAC chain from the root key.
- Check the macaroon's
payment_hash=<hex>caveat againstSHA-256(preimage)so a stolen macaroon does not unlock the resource without the matching preimage. - Run an operator-supplied predicate over the remaining caveats (
valid_until,route,audience, etc.).
The first three checks are stateless: the verifier only needs the root key, no session table.
Macaroons in one paragraph
A macaroon is an HMAC-chained bearer credential. The issuer signs (root_key, identifier) to produce sig_0; every subsequent caveat extends the chain as sig_{i+1} = HMAC(sig_i, caveat_i). The verifier replays the chain from the root key and confirms the final signature matches. Because the chain is one-way, any holder can mint a narrower macaroon by appending a caveat and extending the signature without knowing the root key. The L402 flow uses one issuer-side caveat (payment_hash) and any number of operator-defined caveats; a buyer can attenuate further before delegating to a sub-agent.
Caveats
Operator-defined caveats are opaque byte strings to the verifier; the predicate decides whether each is satisfied at request time. Common shapes:
| Caveat | Predicate |
|---|---|
payment_hash=<hex> | the verifier owns this one; checks SHA-256(preimage) == hex |
valid_until=<unix> | reject when now > unix |
route=<glob> | match the request path against the glob |
audience=<id> | match an authenticated caller id |
scope=<token> | gate per-route capability |
Attenuation
A buyer who already paid for a macaroon can mint a narrower version for a sub-agent by appending a caveat and re-extending the signature chain. The sub-agent presents the same payment_hash preimage on the retry; the verifier's predicate sees the appended caveat and applies the operator's rule. This is how L402 supports delegation without re-paying.
Open design questions
Invoice issuance would sit behind a pluggable seam so the same code could drive an LND, CLN, or Phoenixd backend, with a static stub for local development. Where the verifier plugs into the request pipeline, and how the root key is provisioned and rotated, are unresolved. None of these seams exist yet.
See also
- Lightning Labs L402 specification: https://github.com/lightninglabs/L402
- Aperture reference proxy: https://github.com/lightninglabs/aperture
- Birgisson et al., Macaroons: Cookies with Contextual Caveats: https://research.google/pubs/pub41892/