x402

package
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 24, 2026 License: Apache-2.0 Imports: 39 Imported by: 0

Documentation

Index

Constants

View Source
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

View Source
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

func EnsureVerifier(cfg *config.Config) error

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

func NormalizeNetworkID(network string) string

NormalizeNetworkID maps a human-friendly chain name to its CAIP-2 network identifier. Already-normalized CAIP-2 values are returned as-is.

func PopulateCABundle

func PopulateCABundle(cfg *config.Config)

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

func Setup(cfg *config.Config, wallet, chain, facilitatorURL string) error

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

func ValidateFacilitatorURL(u string) error

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

func ValidateWallet(addr string) error

ValidateWallet checks that addr is a valid 0x-prefixed 20-byte hex Ethereum address.

func WatchConfig

func WatchConfig(ctx context.Context, path string, v *Verifier, interval time.Duration)

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

func WatchConfigWithHandler(ctx context.Context, path string, interval time.Duration, apply func(*PricingConfig) error)

func WatchServiceOffers

func WatchServiceOffers(ctx context.Context, cfg *rest.Config, apply func([]RouteRule) error) error

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

func ResolveChainInfo(name string) (ChainInfo, error)

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

func (v *Verifier) MetricsHandler() http.Handler

MetricsHandler exposes Prometheus metrics for the verifier.

func (*Verifier) Reload

func (v *Verifier) Reload(cfg *PricingConfig) error

Reload atomically swaps the pricing configuration.

Directories

Path Synopsis
Package buyer implements an x402 buyer sidecar that handles payments using pre-signed ERC-3009 TransferWithAuthorization vouchers.
Package buyer implements an x402 buyer sidecar that handles payments using pre-signed ERC-3009 TransferWithAuthorization vouchers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL