Add L402 discovery: manifest + quote endpoints#241
Open
Roasbeef wants to merge 4 commits into
Open
Conversation
In this commit, we factor the core of MintL402 (identifier creation, secret generation, macaroon assembly, and transaction logging) into a shared mintWithChallenge helper, and expose a new MintL402WithCaveats method on top of it. Where MintL402 derives its caveats from the configured ServiceLimiter, MintL402WithCaveats takes an explicit price and caveat set from the caller. This is what the L402 discovery quote endpoint needs: it lets us mint a credential for a client-chosen bundle while reusing the same identifier, secret, and challenge machinery, so the result is indistinguishable from a reactive challenge at verification time.
In this commit, we add a formula pricing model: a base price plus per-unit components that scale with a client's chosen constraint values, computed as basemsat + sum(pricemsatperunit * ceil(value / unit)). The full price is computable by a client from the discovery manifest alone, while the invoice the server issues stays authoritative. The config lives on proxy.Service as a value struct mirroring DynamicPrice, and is surfaced only through the L402 discovery layer. It does not change the reactive 402 price, which still uses Price or DynamicPrice. We also expose Service.ResourcePrice so callers outside the proxy package (the discovery quote endpoint) can query the service's configured pricer.
In this commit, we add the server side of the L402 discovery layer. The new package serves a free static manifest at /.well-known/l402.json that advertises the configured services, their tiers, capabilities, prices (fixed, formula, or dynamic), constraint bounds, and the caveat vocabulary the proxy enforces. It also serves an optional quote endpoint at /l402/quote. A client proposes a bundle (service, capabilities, constraint values, optional budget), and we price it, map it to the macaroon's first-party caveats, and mint a ready-to-pay challenge via MintL402WithCaveats. Because the quote is an ordinary L402 challenge, the credential verifies through the unmodified base path. Both handlers are free, send permissive CORS headers, and return a structured error envelope on failure.
In this commit, we wire the discovery manifest and quote endpoint into the proxy as priority local services, the same mechanism the admin and dashboard endpoints use, so they bypass the 402 gate and are always available. They reuse the same minter as the reactive flow. The sample config documents the new formula pricing option.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In this PR, we add the server side of the L402 discovery layer to aperture, as
specified in lightninglabs/L402#27.
Today aperture is reactive: a client only learns what a service costs by hitting
it and getting a 402 back. Discovery lets a client see the catalog, the prices,
and the caveat vocabulary up front, and gives us a place for dynamic pricing and
the start of in-protocol negotiation.
Two new endpoints are mounted as priority local services, the same mechanism the
admin and dashboard endpoints use, so they bypass the 402 gate and are always
free.
/.well-known/l402.jsonserves a static manifest built from theconfigured services: their capabilities, tiers, prices, constraint bounds, and
the caveat conditions the proxy enforces.
/l402/quotetakes a client-chosenbundle (service, capabilities, constraint values, optional budget), prices it,
maps it to the macaroon's first-party caveats, and mints a ready-to-pay
challenge.
No new verification machinery
The quote endpoint reuses the existing mint and challenger. We factor the core
of
MintL402into a shared helper and addMintL402WithCaveats, which takes anexplicit price and caveat set instead of deriving them from the static
ServiceLimiter. A quote response is just an ordinary L402 challenge minted fora bundle the client chose, so the credential verifies through the unmodified
base path: nothing downstream of the mint changes.
Formula pricing
We add a formula pricing model (
basemsat + sum(pricemsatperunit * ceil(value / unit))) as a value struct onproxy.Service, mirroringDynamicPrice. It issurfaced only through discovery: the manifest advertises it, and the quote
endpoint computes the price from the client's chosen constraint values. The
reactive 402 price is untouched and still uses
PriceorDynamicPrice. Thesample config documents the new option.
The quote endpoint returns a structured error envelope on failure, sends
permissive CORS headers (the manifest carries no secrets), and the discovery
package ships with unit tests covering the manifest build, the pricing models,
the bundle-to-caveat mapping, and the error cases.
BOLT 12 is intentionally not wired here: a macaroon must commit to the payment
hash, which a BOLT 12 offer does not expose until an invoice is fetched, so that
binding needs its own treatment, as noted in the spec.