buyer

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: May 6, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package buyer implements an x402 buyer sidecar that handles payments using pre-signed ERC-3009 TransferWithAuthorization vouchers. The sidecar acts as an OpenAI-compatible reverse proxy — it intercepts 402 responses from upstream sellers, attaches pre-signed payment headers, and retries automatically.

The agent pre-signs a bounded batch of authorizations and stores them in a ConfigMap. The sidecar reads from this pool and has zero signer access. Spending is bounded by design: max loss = N * price.

Index

Constants

View Source
const (
	ErrCodeInvalidRequirements = "invalid_requirements"
	ErrCodeSigningFailed       = "signing_failed"
)

Error code constants.

Variables

View Source
var ErrNoValidSigner = errors.New("no valid signer found for payment requirements")

ErrNoValidSigner is returned when no signer in the pool can satisfy any requirement.

Functions

func EncodePayment added in v0.8.0

func EncodePayment(payment x402types.PaymentPayload) (string, error)

EncodePayment converts a v2 PaymentPayload to a base64-encoded JSON string for the X-PAYMENT HTTP header.

func NewPaymentError added in v0.8.0

func NewPaymentError(code, msg string, err error) error

NewPaymentError creates a PaymentError with the given code and message.

Types

type AuthsFile

type AuthsFile map[string][]*PreSignedAuth

AuthsFile is the top-level structure for the pre-signed authorizations file, loaded from the x402-buyer-auths ConfigMap. Keys are upstream names matching Config.Upstreams.

func LoadAuths

func LoadAuths(path string) (AuthsFile, error)

LoadAuths reads and parses the pre-signed authorizations from a JSON file.

func LoadAuthsDir added in v0.8.0

func LoadAuthsDir(dir string) (AuthsFile, error)

LoadAuthsDir reads per-upstream auth files from a directory. Each *.json file contains an array of PreSignedAuth for one upstream.

type Config

type Config struct {
	Upstreams map[string]UpstreamConfig `json:"upstreams"`
}

Config is the top-level sidecar configuration, loaded from a JSON file mounted from the x402-buyer-config ConfigMap.

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig reads and parses the sidecar config from a JSON file.

func LoadConfigDir added in v0.8.0

func LoadConfigDir(dir string) (*Config, error)

LoadConfigDir reads per-upstream config files from a directory. Each *.json file is one upstream, keyed by the filename stem (e.g. "42.json" → key "42"). This is the SSA-compatible format where the controller applies one key per PurchaseRequest via Server-Side Apply.

type DefaultPaymentSelector added in v0.8.0

type DefaultPaymentSelector struct{}

DefaultPaymentSelector iterates signers by priority and picks the first match.

func (*DefaultPaymentSelector) SelectAndSign added in v0.8.0

func (s *DefaultPaymentSelector) SelectAndSign(requirements []x402types.PaymentRequirements, signers []Signer) (*x402types.PaymentPayload, error)

SelectAndSign finds the first signer that can satisfy any requirement and signs it.

type PaymentCallback added in v0.8.0

type PaymentCallback func(PaymentEvent)

PaymentCallback receives payment lifecycle events.

type PaymentError added in v0.8.0

type PaymentError struct {
	Code    string
	Message string
	Err     error
}

PaymentError wraps an error with an x402-specific error code.

func (*PaymentError) Error added in v0.8.0

func (e *PaymentError) Error() string

func (*PaymentError) Unwrap added in v0.8.0

func (e *PaymentError) Unwrap() error

type PaymentEvent added in v0.8.0

type PaymentEvent struct {
	Type        PaymentEventType
	Timestamp   time.Time
	Duration    time.Duration
	Method      string
	URL         string
	Network     string
	Scheme      string
	Amount      string
	Asset       string
	Recipient   string
	Transaction string
	Payer       string
	Error       error
}

PaymentEvent is emitted by the buyer transport for Prometheus instrumentation.

type PaymentEventType added in v0.8.0

type PaymentEventType string

PaymentEventType identifies the kind of payment event.

const (
	PaymentEventAttempt PaymentEventType = "attempt"
	PaymentEventSuccess PaymentEventType = "success"
	PaymentEventFailure PaymentEventType = "failure"
	// PaymentEventUnsettled indicates the upstream returned 2xx without a
	// successful X-PAYMENT-RESPONSE. The auth was consumed locally but no
	// on-chain settlement has been observed.
	PaymentEventUnsettled PaymentEventType = "unsettled"
)

type PaymentSelector added in v0.8.0

type PaymentSelector interface {
	SelectAndSign(requirements []x402types.PaymentRequirements, signers []Signer) (*x402types.PaymentPayload, error)
}

PaymentSelector picks a requirement and signs it.

func NewDefaultPaymentSelector added in v0.8.0

func NewDefaultPaymentSelector() PaymentSelector

NewDefaultPaymentSelector returns a DefaultPaymentSelector.

type PreSignedAuth

type PreSignedAuth struct {
	ID          string                    `json:"id,omitempty"`
	Payment     *x402types.PaymentPayload `json:"payment,omitempty"`
	Signature   string                    `json:"signature"`
	From        string                    `json:"from"`
	To          string                    `json:"to"`
	Value       string                    `json:"value"`
	ValidAfter  string                    `json:"validAfter"`
	ValidBefore string                    `json:"validBefore"`
	Nonce       string                    `json:"nonce"`
}

PreSignedAuth is a queued signed x402 payment. Legacy ERC-3009 auth fields are still supported for backward compatibility, but new entries should prefer the fully formed Payment payload.

func (*PreSignedAuth) ConsumeKey added in v0.9.0

func (a *PreSignedAuth) ConsumeKey() string

type PreSignedSigner

type PreSignedSigner struct {
	// contains filtered or unexported fields
}

PreSignedSigner implements Signer using pre-signed ERC-3009 TransferWithAuthorization vouchers. It pops one auth from the pool per Sign() call. The pool is finite — once exhausted, CanSign returns false.

Thread-safe via sync.Mutex.

func NewPreSignedSigner

func NewPreSignedSigner(network, payTo, asset, price, symbol string, decimals int, auths []*PreSignedAuth, spent int, onConsume func(*PreSignedAuth) error) *PreSignedSigner

NewPreSignedSigner creates a signer backed by a pool of pre-signed auths.

func (*PreSignedSigner) CanSign

CanSign checks if this signer can satisfy the given payment requirement. Returns true if network, payTo, asset, and amount match and there are remaining auths in the pool.

func (*PreSignedSigner) ConfirmSpend added in v0.8.0

func (s *PreSignedSigner) ConfirmSpend(auth *PreSignedAuth) error

ConfirmSpend persists a nonce as consumed after a successful paid upstream response. The auth must be the pointer returned from HoldSign for this hold.

func (*PreSignedSigner) GetMaxAmount

func (s *PreSignedSigner) GetMaxAmount() *big.Int

GetMaxAmount returns nil (no per-call limit — bounded by pool size instead).

func (*PreSignedSigner) GetPriority

func (s *PreSignedSigner) GetPriority() int

GetPriority returns 0 (highest priority).

func (*PreSignedSigner) GetTokens

func (s *PreSignedSigner) GetTokens() []TokenConfig

GetTokens returns the single USDC token this signer handles.

func (*PreSignedSigner) HoldSign added in v0.8.0

HoldSign removes one auth from the pool and builds a payment payload without persisting consume (no onConsume). The caller must invoke exactly one of ConfirmSpend or ReleaseSpend with the returned auth.

func (*PreSignedSigner) Network

func (s *PreSignedSigner) Network() string

Network returns the blockchain network this signer operates on.

func (*PreSignedSigner) ReleaseSpend added in v0.8.0

func (s *PreSignedSigner) ReleaseSpend(auth *PreSignedAuth)

ReleaseSpend returns a held auth to the pool after a failed payment attempt (network error or upstream HTTP error). It reverses HoldSign's in-memory reservation so the voucher can be retried.

func (*PreSignedSigner) Remaining

func (s *PreSignedSigner) Remaining() int

Remaining returns the number of pre-signed authorizations left in the pool.

func (*PreSignedSigner) Scheme

func (s *PreSignedSigner) Scheme() string

Scheme returns "exact" — the only payment scheme for EVM x402.

func (*PreSignedSigner) Sign

Sign pops one pre-signed authorization from the pool and returns it as a PaymentPayload, then persists local consume only after ConfirmSpend succeeds. Returns an error when the pool is exhausted.

func (*PreSignedSigner) Spent

func (s *PreSignedSigner) Spent() int

Spent returns the number of authorizations consumed so far.

type Proxy

type Proxy struct {
	// contains filtered or unexported fields
}

Proxy is an OpenAI-compatible reverse proxy that routes requests to upstream x402-gated endpoints, attaching pre-signed payment headers automatically.

Routing:

  • OpenAI-compatible chat/responses paths resolve the upstream from the requested model.
  • /upstream/<name>/... remains available for compatibility.

func NewProxy

func NewProxy(cfg *Config, auths AuthsFile, state *StateStore) (*Proxy, error)

NewProxy creates a proxy from the given config and auth pools.

func (*Proxy) Reload added in v0.7.0

func (p *Proxy) Reload(cfg *Config, auths AuthsFile) error

Reload atomically rebuilds the upstream handlers from config/auth sources.

func (*Proxy) ReloadCh added in v0.8.0

func (p *Proxy) ReloadCh() <-chan struct{}

func (*Proxy) ServeHTTP

func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP dispatches to the internal mux.

type SettlementResponse added in v0.8.0

type SettlementResponse struct {
	Success     bool   `json:"success"`
	ErrorReason string `json:"errorReason,omitempty"`
	Transaction string `json:"transaction,omitempty"`
	Network     string `json:"network"`
	Payer       string `json:"payer"`
}

SettlementResponse is the decoded X-PAYMENT-RESPONSE header.

func DecodeSettlement added in v0.8.0

func DecodeSettlement(encoded string) (SettlementResponse, error)

DecodeSettlement decodes a base64-encoded X-PAYMENT-RESPONSE header.

type Signer added in v0.8.0

type Signer interface {
	Network() string
	Scheme() string
	CanSign(req *x402types.PaymentRequirements) bool
	Sign(req *x402types.PaymentRequirements) (*x402types.PaymentPayload, error)
	GetPriority() int
	GetTokens() []TokenConfig
	GetMaxAmount() *big.Int
}

Signer produces x402 v2 payment payloads for a specific network and scheme. The buyer proxy holds an array of signers and selects the first one that can satisfy an incoming 402's requirements.

type StateStore added in v0.7.0

type StateStore struct {
	// contains filtered or unexported fields
}

StateStore tracks consumed authorization nonces so hot reloads and restarts do not reintroduce already-spent vouchers from the ConfigMap source.

func LoadStateStore added in v0.7.0

func LoadStateStore(path string) (*StateStore, error)

LoadStateStore loads consumed authorization state from disk. Missing files are treated as an empty state.

func (*StateStore) ConsumedCount added in v0.7.0

func (s *StateStore) ConsumedCount(upstream string) int

ConsumedCount returns the number of consumed authorizations for an upstream.

func (*StateStore) IsConsumed added in v0.7.0

func (s *StateStore) IsConsumed(upstream, nonce string) bool

IsConsumed reports whether a nonce was already consumed for an upstream.

func (*StateStore) MarkConsumed added in v0.7.0

func (s *StateStore) MarkConsumed(upstream, nonce string) error

MarkConsumed records a consumed authorization nonce and persists the updated state to disk.

type TokenConfig added in v0.8.0

type TokenConfig struct {
	Address  string `json:"address"`
	Symbol   string `json:"symbol"`
	Decimals int    `json:"decimals"`
	Priority int    `json:"priority"`
}

TokenConfig describes a token a signer can pay with.

type UpstreamConfig

type UpstreamConfig struct {
	// URL is the upstream base URL (e.g. "https://seller.example.com/services/qwen").
	URL string `json:"url"`

	// RemoteModel is the concrete upstream model served by this purchased route.
	// The LiteLLM paid/* namespace resolves to this model before the sidecar
	// forwards the request to the seller.
	RemoteModel string `json:"remoteModel,omitempty"`

	// Network is the blockchain network identifier (e.g. "base-sepolia").
	Network string `json:"network"`

	// PayTo is the seller's receiving address.
	PayTo string `json:"payTo"`

	// Asset is the token contract address (e.g. USDC on Base Sepolia).
	Asset string `json:"asset"`

	// AssetSymbol is the human-friendly token symbol (e.g. USDC, OBOL).
	AssetSymbol string `json:"assetSymbol,omitempty"`

	// AssetDecimals is the token precision in atomic units.
	AssetDecimals int `json:"assetDecimals,omitempty"`

	// AssetTransferMethod is the x402 transfer method (eip3009 or permit2).
	AssetTransferMethod string `json:"assetTransferMethod,omitempty"`

	// EIP712Name is the EIP-712 domain name for the token or permit flow.
	EIP712Name string `json:"eip712Name,omitempty"`

	// EIP712Version is the EIP-712 domain version for the token or permit flow.
	EIP712Version string `json:"eip712Version,omitempty"`

	// Price is the amount in atomic units per request (e.g. "1000" for 0.001 USDC).
	Price string `json:"price"`
}

UpstreamConfig describes a single x402-gated upstream endpoint.

Jump to

Keyboard shortcuts

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