Documentation
¶
Index ¶
- Constants
- Variables
- func BuildV1Requirement(chain ChainInfo, amount, recipientAddress string) x402types.PaymentRequirementsV1
- func BuildV2Requirement(chain ChainInfo, amount, recipientAddress string) x402types.PaymentRequirements
- func EnsureVerifier(cfg *config.Config) error
- func NewForwardAuthMiddleware(cfg ForwardAuthConfig, requirements []x402types.PaymentRequirements) func(http.Handler) http.Handler
- func NormalizeNetworkID(network string) string
- func PopulateCABundle(cfg *config.Config)
- func Setup(cfg *config.Config, wallet, chain, facilitatorURL string) error
- func ValidateFacilitatorURL(u string) error
- func ValidateWallet(addr string) error
- func WatchConfig(ctx context.Context, path string, v *Verifier, interval time.Duration)
- func WatchConfigWithHandler(ctx context.Context, path string, interval time.Duration, ...)
- func WatchServiceOffers(ctx context.Context, cfg *rest.Config, apply func([]RouteRule) error) error
- type ChainInfo
- type ConfigAccumulator
- type ForwardAuthConfig
- type PricingConfig
- type RouteRule
- type Verifier
- func (v *Verifier) HandleHealthz(w http.ResponseWriter, r *http.Request)
- func (v *Verifier) HandleProxy(w http.ResponseWriter, r *http.Request)
- func (v *Verifier) HandleReadyz(w http.ResponseWriter, r *http.Request)
- func (v *Verifier) HandleVerify(w http.ResponseWriter, r *http.Request)
- func (v *Verifier) MetricsHandler() http.Handler
- func (v *Verifier) Reload(cfg *PricingConfig) error
Constants ¶
const ( // DefaultFacilitatorURL is the Obol-operated x402 facilitator for payment // verification and settlement. Supports Base Mainnet and Base Sepolia. DefaultFacilitatorURL = "https://x402.gcp.obol.tech" )
Variables ¶
var ( ChainBaseMainnet = ChainInfo{ Name: "base", NetworkID: "base", CAIP2Network: "eip155:8453", USDCAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", Decimals: 6, EIP3009Name: "USD Coin", EIP3009Version: "2", } ChainBaseSepolia = ChainInfo{ Name: "base-sepolia", NetworkID: "base-sepolia", CAIP2Network: "eip155:84532", USDCAddress: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", Decimals: 6, EIP3009Name: "USD Coin", EIP3009Version: "2", } ChainEthereumMainnet = ChainInfo{ Name: "ethereum", NetworkID: "ethereum", CAIP2Network: "eip155:1", USDCAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", Decimals: 6, EIP3009Name: "USD Coin", EIP3009Version: "2", } ChainPolygonMainnet = ChainInfo{ Name: "polygon", NetworkID: "polygon", CAIP2Network: "eip155:137", USDCAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", Decimals: 6, EIP3009Name: "USD Coin", EIP3009Version: "2", } ChainPolygonAmoy = ChainInfo{ Name: "polygon-amoy", NetworkID: "polygon-amoy", CAIP2Network: "eip155:80002", USDCAddress: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582", Decimals: 6, EIP3009Name: "USD Coin", EIP3009Version: "2", } ChainAvalancheMainnet = ChainInfo{ Name: "avalanche", NetworkID: "avalanche", CAIP2Network: "eip155:43114", USDCAddress: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", Decimals: 6, EIP3009Name: "USD Coin", EIP3009Version: "2", } ChainAvalancheFuji = ChainInfo{ Name: "avalanche-fuji", NetworkID: "avalanche-fuji", CAIP2Network: "eip155:43113", USDCAddress: "0x5425890298aed601595a70AB815c96711a31Bc65", Decimals: 6, EIP3009Name: "USD Coin", EIP3009Version: "2", } ChainArbitrumOne = ChainInfo{ Name: "arbitrum-one", NetworkID: "arbitrum-one", CAIP2Network: "eip155:42161", USDCAddress: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", Decimals: 6, EIP3009Name: "USD Coin", EIP3009Version: "2", } ChainArbitrumSepolia = ChainInfo{ Name: "arbitrum-sepolia", NetworkID: "arbitrum-sepolia", CAIP2Network: "eip155:421614", USDCAddress: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d", Decimals: 6, EIP3009Name: "USD Coin", EIP3009Version: "2", } )
Chain constants — USDC addresses verified against coinbase/x402/go v2.7.0 mechanisms/evm/constants.go and on-chain contract deployments.
Functions ¶
func BuildV1Requirement ¶
func BuildV1Requirement(chain ChainInfo, amount, recipientAddress string) x402types.PaymentRequirementsV1
BuildV1Requirement creates a v1 PaymentRequirementsV1 for USDC payment on the given chain. amount is the decimal USDC amount (e.g., "0.001" = $0.001).
func BuildV2Requirement ¶
func BuildV2Requirement(chain ChainInfo, amount, recipientAddress string) x402types.PaymentRequirements
BuildV2Requirement creates a v2 PaymentRequirements for USDC payment on the given chain. amount is the decimal USDC amount (e.g. "0.001" = $0.001).
func EnsureVerifier ¶
EnsureVerifier deploys the x402 verifier subsystem if it doesn't exist. Idempotent — kubectl apply is safe to run multiple times.
func NewForwardAuthMiddleware ¶
func NewForwardAuthMiddleware(cfg ForwardAuthConfig, requirements []x402types.PaymentRequirements) func(http.Handler) http.Handler
NewForwardAuthMiddleware creates an x402 payment-gating middleware compatible with the v1 wire format. It checks the X-PAYMENT header, verifies the payment with the facilitator, and optionally settles after a successful downstream response.
When VerifyOnly is true (Traefik ForwardAuth path), settlement is skipped. When VerifyOnly is false (standalone gateway path), settlement runs only after the inner handler returns a success status (< 400).
func NormalizeNetworkID ¶
NormalizeNetworkID maps a human-friendly chain name to its CAIP-2 network identifier. Already-normalized CAIP-2 values are returned as-is.
func PopulateCABundle ¶
PopulateCABundle reads the host's CA certificate bundle and replaces the ca-certificates ConfigMap in the x402 namespace. Call this whenever the x402 verifier is deployed or updated without going through EnsureVerifier. Silently skips if no CA bundle is found on the host.
func Setup ¶
Setup configures x402 pricing in the cluster by patching the ConfigMap and Secret. Stakater Reloader auto-restarts the verifier pod. If facilitatorURL is empty, the Obol-operated facilitator is used.
func ValidateFacilitatorURL ¶
ValidateFacilitatorURL checks that the facilitator URL uses HTTPS. Payment proofs sent over plain HTTP could be intercepted. Loopback addresses (localhost, 127.0.0.1, [::1]) and k3d/Docker internal addresses are exempted for local development and testing.
func ValidateWallet ¶
ValidateWallet checks that addr is a valid 0x-prefixed 20-byte hex Ethereum address.
func WatchConfig ¶
WatchConfig polls a YAML config file for changes and reloads the Verifier when the file is modified. It checks the file's modification time every interval. This handles ConfigMap volume mount updates (kubelet symlink swaps) without requiring fsnotify.
WatchConfig blocks until the context is cancelled.
func WatchConfigWithHandler ¶
Types ¶
type ChainInfo ¶
type ChainInfo struct {
// Name is the human-friendly identifier used by the CLI (e.g., "base-sepolia").
Name string
// NetworkID is the v1 wire-format network name sent to the facilitator.
NetworkID string
// CAIP2Network is the v2 wire-format network identifier.
CAIP2Network string
// USDCAddress is the USDC token contract address on this chain.
USDCAddress string
// Decimals is the token decimal precision (6 for USDC).
Decimals int
// EIP3009Name is the EIP-712 domain name for TransferWithAuthorization.
EIP3009Name string
// EIP3009Version is the EIP-712 domain version.
EIP3009Version string
}
ChainInfo holds chain-specific configuration for x402 payment gating. It maps a human-friendly chain name to the on-wire identifiers the facilitator expects (v1 network names, USDC contract address, EIP-3009 domain parameters).
func ResolveChainInfo ¶
ResolveChainInfo maps a human-friendly chain name to its ChainInfo. Phase 2 renames this to ResolveChain after deleting the old one in config.go.
type ConfigAccumulator ¶
type ConfigAccumulator struct {
// contains filtered or unexported fields
}
func NewConfigAccumulator ¶
func NewConfigAccumulator(base *PricingConfig, verifier *Verifier) *ConfigAccumulator
func (*ConfigAccumulator) SetBase ¶
func (a *ConfigAccumulator) SetBase(base *PricingConfig) error
func (*ConfigAccumulator) SetRoutes ¶
func (a *ConfigAccumulator) SetRoutes(routes []RouteRule) error
type ForwardAuthConfig ¶
type ForwardAuthConfig struct {
// FacilitatorURL is the x402 facilitator service URL (e.g., "https://x402.org/facilitator").
FacilitatorURL string
// VerifyOnly skips blockchain settlement when true. Used by the Traefik
// ForwardAuth verifier where only payment verification is needed.
//
// INVARIANT: VerifyOnly MUST be true whenever this middleware is used
// behind Traefik ForwardAuth. The auth hop runs before the upstream is
// contacted and cannot observe the upstream's status; settling there
// debits the payer before the upstream has proven it served the request.
// VerifyOnly=false is only safe for in-process middleware (e.g. the
// standalone inference gateway) that sees the real upstream status.
//
// NewForwardAuthMiddleware logs a loud warning when VerifyOnly is false
// so operators who flip this in x402-pricing.yaml notice in logs.
VerifyOnly bool
}
ForwardAuthConfig configures the ForwardAuth x402 middleware.
type PricingConfig ¶
type PricingConfig struct {
// Wallet is the USDC recipient address for all payments.
Wallet string `yaml:"wallet"`
// Chain is the blockchain network name (e.g., "base-sepolia", "base").
Chain string `yaml:"chain"`
// FacilitatorURL is the x402 facilitator service URL.
FacilitatorURL string `yaml:"facilitatorURL"`
// VerifyOnly skips blockchain settlement after successful verification.
VerifyOnly bool `yaml:"verifyOnly"`
// Routes defines per-route pricing rules. First match wins.
Routes []RouteRule `yaml:"routes"`
}
PricingConfig is the top-level configuration for the x402 ForwardAuth verifier. It defines global payment parameters and per-route pricing rules.
func GetPricingConfig ¶
func GetPricingConfig(cfg *config.Config) (*PricingConfig, error)
GetPricingConfig reads the current x402 pricing ConfigMap from the cluster.
func LoadConfig ¶
func LoadConfig(path string) (*PricingConfig, error)
LoadConfig reads and parses a pricing configuration YAML file.
type RouteRule ¶
type RouteRule struct {
// Pattern is a path matching pattern. Supports:
// - Exact match: "/health"
// - Prefix match: "/rpc/*" (matches /rpc/anything)
// - Glob match: "/inference-*/v1/*"
Pattern string `yaml:"pattern"`
// Price is the USDC amount per request (e.g., "0.0001").
Price string `yaml:"price"`
// Description is a human-readable label for this route (optional).
Description string `yaml:"description"`
// PayTo overrides the global wallet for this route (x402: payTo).
// If empty, falls back to PricingConfig.Wallet.
PayTo string `yaml:"payTo,omitempty"`
// Network overrides the global chain for this route (human-friendly).
// If empty, falls back to PricingConfig.Chain.
Network string `yaml:"network,omitempty"`
// UpstreamAuth is injected as the Authorization header on approved requests.
// The shared seller gateway injects this header on approved upstream
// requests. This lets the upstream (e.g., LiteLLM) authenticate the request
// without exposing the key to buyers.
UpstreamAuth string `yaml:"upstreamAuth,omitempty"`
// UpstreamURL is the in-cluster HTTP base URL for the protected upstream.
// The shared seller gateway proxies matched paid routes to this target after
// successful payment verification.
UpstreamURL string `yaml:"upstreamURL,omitempty"`
// StripPrefix is removed from the incoming request path before proxying to
// the upstream. For ServiceOffers this is usually /services/<offer-name>.
StripPrefix string `yaml:"stripPrefix,omitempty"`
// PriceModel records which price field produced the enforced request price.
// It is metadata only; the verifier always enforces Price.
PriceModel string `yaml:"priceModel,omitempty"`
// PerMTok stores the original per-million-token price when Price was
// approximated for phase 1 request-based gating.
PerMTok string `yaml:"perMTok,omitempty"`
// ApproxTokensPerRequest stores the fixed token estimate used to derive
// Price from PerMTok during phase 1.
ApproxTokensPerRequest int `yaml:"approxTokensPerRequest,omitempty"`
// OfferNamespace identifies the originating ServiceOffer namespace.
OfferNamespace string `yaml:"offerNamespace,omitempty"`
// OfferName identifies the originating ServiceOffer name.
OfferName string `yaml:"offerName,omitempty"`
}
RouteRule maps a URL pattern to x402 payment requirements. Per-route fields (PayTo, Network) override the global PricingConfig values when set, enabling multiple ServiceOffers with different wallets/chains.
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier is a ForwardAuth-compatible HTTP handler that enforces x402 micropayments on a per-route basis. Traefik sends every incoming request to /verify; the Verifier either returns 200 (allow) or 402 (pay-wall).
func NewVerifier ¶
func NewVerifier(cfg *PricingConfig) (*Verifier, error)
NewVerifier creates a Verifier with the given initial configuration.
func (*Verifier) HandleHealthz ¶
func (v *Verifier) HandleHealthz(w http.ResponseWriter, r *http.Request)
HandleHealthz returns 200 OK for liveness probes.
func (*Verifier) HandleProxy ¶
func (v *Verifier) HandleProxy(w http.ResponseWriter, r *http.Request)
HandleProxy serves the seller-owned paid route directly. It matches the incoming path to a ServiceOffer-derived route rule, verifies the payment, proxies to the real upstream, and settles only after the upstream succeeds.
func (*Verifier) HandleReadyz ¶
func (v *Verifier) HandleReadyz(w http.ResponseWriter, r *http.Request)
HandleReadyz returns 200 OK if pricing config is loaded, 503 otherwise.
func (*Verifier) HandleVerify ¶
func (v *Verifier) HandleVerify(w http.ResponseWriter, r *http.Request)
HandleVerify is the ForwardAuth endpoint. Traefik forwards the original request headers; the Verifier inspects X-Forwarded-Uri to determine which pricing rule applies.
Response semantics (ForwardAuth contract):
- 200: allow the request through to the backend
- 402: deny with x402 payment requirements in the response body
- 500: internal error (Traefik returns 500 to the client)
func (*Verifier) MetricsHandler ¶
MetricsHandler exposes Prometheus metrics for the verifier.
func (*Verifier) Reload ¶
func (v *Verifier) Reload(cfg *PricingConfig) error
Reload atomically swaps the pricing configuration.