buyer

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Apr 23, 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

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

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

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

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

type DefaultPaymentSelector struct{}

DefaultPaymentSelector iterates signers by priority and picks the first match.

func (*DefaultPaymentSelector) SelectAndSign

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

type PaymentCallback func(PaymentEvent)

PaymentCallback receives payment lifecycle events.

type PaymentError

type PaymentError struct {
	Code    string
	Message string
	Err     error
}

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

func (*PaymentError) Error

func (e *PaymentError) Error() string

func (*PaymentError) Unwrap

func (e *PaymentError) Unwrap() error

type PaymentEvent

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

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

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

PaymentSelector picks a requirement and signs it.

func NewDefaultPaymentSelector

func NewDefaultPaymentSelector() PaymentSelector

NewDefaultPaymentSelector returns a DefaultPaymentSelector.

type PreSignedAuth

type PreSignedAuth struct {
	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 single pre-signed ERC-3009 TransferWithAuthorization voucher. Each voucher is single-use — consumed when the facilitator calls settle() on-chain.

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 string, 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

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

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

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

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

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

func (*Proxy) ReloadCh

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

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

func DecodeSettlement(encoded string) (SettlementResponse, error)

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

type Signer

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

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

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

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

func (*StateStore) ConsumedCount

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

ConsumedCount returns the number of consumed authorizations for an upstream.

func (*StateStore) IsConsumed

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

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

func (*StateStore) MarkConsumed

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

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

type TokenConfig

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"`

	// 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