Class: X402::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/x402/configuration.rb

Overview

DSL for configuring X402 middleware, gateways, and protected routes.

Examples:

Minimal configuration (relay mode — no keys on server)

X402.configure do |config|
  config.domain = "api.example.com"
  config.operator_wallet_url = "https://my-wallet.example.com/api/server-wallet"
  config.protect method: :GET, path: "/api/expensive", amount_sats: 100
end

Defined Under Namespace

Classes: Route

Constant Summary collapse

GATEWAY_METHODS =
%i[challenge_headers proof_header_names settle!].freeze
PAY_GATEWAY_KNOWN_OPTS =
%i[
  arc_client payee_locking_script_hex arc_wait_for arc_timeout
  binding_mode wallet challenge_secret settlement_worker txid_store
].freeze
PROOF_GATEWAY_KNOWN_OPTS =
%i[
  arc_client payee_locking_script_hex nonce_provider
  wallet challenge_secret
].freeze
BRC105_GATEWAY_KNOWN_OPTS =
%i[
  wallet key_deriver server_wif server_key prefix_store arc_client
].freeze
BRC121_GATEWAY_KNOWN_OPTS =
%i[wallet txid_store arc_client].freeze
GATEWAY_REGISTRY =
{
  pay_gateway: "X402::BSV::PayGateway",
  proof_gateway: "X402::BSV::ProofGateway",
  brc105_gateway: "X402::BSV::BRC105Gateway",
  brc121_gateway: "X402::BSV::BRC121Gateway"
}.freeze
DEFAULT_GATEWAYS =

Gateways auto-enabled when +wallet:+ is set and no explicit +enable+ or +gateways=+ calls were made. Both work zero-config with a BRC-100 wallet and expose non-overlapping proof headers.

%i[pay_gateway brc121_gateway].freeze
VALID_NETWORKS =
%w[bsv:mainnet bsv:testnet].freeze
DEFAULT_NETWORK =
"bsv:mainnet"
DEFAULT_STATUS_ENDPOINT_PATH =
"/_x402/status"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



68
69
70
71
72
73
74
75
76
77
# File 'lib/x402/configuration.rb', line 68

def initialize
  @routes = []
  @gateways = []
  @gateway_specs = []
  @network = DEFAULT_NETWORK
  @logger = default_logger
  @status_endpoint_enabled = false
  @status_endpoint_path = DEFAULT_STATUS_ENDPOINT_PATH
  @status_endpoint_token = nil
end

Instance Attribute Details

#arc_api_keyObject

Returns the value of attribute arc_api_key.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def arc_api_key
  @arc_api_key
end

#arc_clientObject

Returns the value of attribute arc_client.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def arc_client
  @arc_client
end

#arc_urlObject

Returns the value of attribute arc_url.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def arc_url
  @arc_url
end

#domainObject

Returns the value of attribute domain.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def domain
  @domain
end

#exchange_rate_providerObject

Returns the value of attribute exchange_rate_provider.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def exchange_rate_provider
  @exchange_rate_provider
end

#gateway_specsObject (readonly)

Returns the value of attribute gateway_specs.



64
65
66
# File 'lib/x402/configuration.rb', line 64

def gateway_specs
  @gateway_specs
end

#gatewaysObject

Returns the value of attribute gateways.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def gateways
  @gateways
end

#loggerObject

Returns the value of attribute logger.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def logger
  @logger
end

#networkObject

Returns the value of attribute network.



64
65
66
# File 'lib/x402/configuration.rb', line 64

def network
  @network
end

#operator_wallet_urlObject

Returns the value of attribute operator_wallet_url.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def operator_wallet_url
  @operator_wallet_url
end

#payee_locking_script_hexObject

Returns the value of attribute payee_locking_script_hex.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def payee_locking_script_hex
  @payee_locking_script_hex
end

#routesObject (readonly)

Returns the value of attribute routes.



64
65
66
# File 'lib/x402/configuration.rb', line 64

def routes
  @routes
end

#server_wifObject

Returns the value of attribute server_wif.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def server_wif
  @server_wif
end

#status_endpoint_pathObject

Returns the value of attribute status_endpoint_path.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def status_endpoint_path
  @status_endpoint_path
end

#status_endpoint_tokenObject

Returns the value of attribute status_endpoint_token.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def status_endpoint_token
  @status_endpoint_token
end

#walletObject

Returns the value of attribute wallet.



60
61
62
# File 'lib/x402/configuration.rb', line 60

def wallet
  @wallet
end

Instance Method Details

#enable(name, **options) ⇒ Object

Record a gateway to be constructed later during +validate!+.

Parameters:

  • name (Symbol)

    registered gateway name (e.g. +:pay_gateway+)

  • options (Hash)

    options forwarded to the gateway constructor

Raises:



124
125
126
127
128
129
# File 'lib/x402/configuration.rb', line 124

def enable(name, **options)
  class_name = GATEWAY_REGISTRY[name]
  raise ConfigurationError, "unknown gateway: #{name.inspect}" unless class_name

  @gateway_specs << [class_name, options]
end

#enable_status_endpointvoid

This method returns an undefined value.

Opt in to the read-only status endpoint at +status_endpoint_path+ (default +/_x402/status+). Disabled by default.

The endpoint is always bearer-token authenticated. If +status_endpoint_token+ is unset at +validate!+ time, a random token is generated and logged once at startup so it can be copied into a +curl+ command. Production deployments should set +status_endpoint_token+ explicitly (typically from an environment variable) to suppress auto-generation.



90
91
92
# File 'lib/x402/configuration.rb', line 90

def enable_status_endpoint
  @status_endpoint_enabled = true
end

#find_route(request_method, request_path) ⇒ Route?

Find the matching route for a request method and path.

Returns:



187
188
189
190
191
192
# File 'lib/x402/configuration.rb', line 187

def find_route(request_method, request_path)
  @routes.find do |route|
    method_matches?(route.http_method, request_method) &&
      path_matches?(route.path, request_path)
  end
end

#protect(method:, path:, amount_sats: nil, amount_usd: nil, arc_wait_for: nil) ⇒ Object

Register a protected route.

Parameters:

  • method (String)

    HTTP method or "*" for any

  • path (String, Regexp)

    exact path or pattern

  • amount_sats (Integer, #call) (defaults to: nil)

    required payment in satoshis. Accepts a static Integer or a callable (Proc/Lambda) that returns the current sats amount at challenge time. Use a callable for fiat-denominated pricing with live exchange rates.

  • amount_usd (Numeric, nil) (defaults to: nil)

    convenience — price in USD, resolved to sats at challenge time via +exchange_rate_provider+. Mutually exclusive with +amount_sats+.

  • arc_wait_for (String, Symbol, nil) (defaults to: nil)

    per-route ARC settlement override. +nil+ (default) uses the gateway's +arc_wait_for+ setting. A string value (e.g. +"SEEN_ON_NETWORK"+, +"MINED"+) overrides the gateway default for synchronous broadcast. +:async+ validates the transaction locally then enqueues it for background settlement via the gateway's +settlement_worker+, returning 200 immediately without waiting for ARC confirmation.



179
180
181
182
# File 'lib/x402/configuration.rb', line 179

def protect(method:, path:, amount_sats: nil, amount_usd: nil, arc_wait_for: nil)
  sats = resolve_amount(amount_sats, amount_usd)
  @routes << Route.new(http_method: method.upcase, path: path, amount_sats: sats, arc_wait_for: arc_wait_for)
end

#shared_arc_clientBSV::Network::ARC

Returns a memoised ARC client instance. If +arc_client+ has been injected directly, that takes precedence. Otherwise, builds one from +arc_url+ (and optional +arc_api_key+), falling back to +ARC.default+ when no explicit URL is configured.

Returns:

  • (BSV::Network::ARC)


137
138
139
140
141
# File 'lib/x402/configuration.rb', line 137

def shared_arc_client
  return @arc_client if @arc_client

  @shared_arc_client ||= build_arc_client
end

#shared_walletBSV::Wallet::ProtoWallet, ...

Returns the shared wallet used by all gateways for key derivation (BRC-42/43) and, where supported, payment internalisation.

Resolution order:

  1. An explicitly-set +@wallet+ (any BRC-100 compatible wallet, typically a +BSV::Wallet::WalletClient+), or a +RemoteWallet+ auto-constructed from +operator_wallet_url+
  2. A +ProtoWallet+ built from +server_wif+ (backwards compat)
  3. +nil+ if neither is set

Returns:

  • (BSV::Wallet::ProtoWallet, BSV::Wallet::WalletClient, RemoteWallet, nil)


154
155
156
157
158
159
# File 'lib/x402/configuration.rb', line 154

def shared_wallet
  return @wallet if @wallet
  return if @server_wif.nil? || @server_wif.empty?

  @shared_wallet ||= build_wallet
end

#status_endpoint_enabled?Boolean

Returns whether the status endpoint should be served.

Returns:

  • (Boolean)

    whether the status endpoint should be served



95
96
97
# File 'lib/x402/configuration.rb', line 95

def status_endpoint_enabled?
  @status_endpoint_enabled
end

#validate!Object

Validate the configuration and construct gateways from specs.

Called automatically at the end of +X402.configure+. Builds gateways from +enable+ specs, validates all constraints, and emits operational warnings for development defaults.

Raises:



201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/x402/configuration.rb', line 201

def validate!
  raise ConfigurationError, "domain is required" if domain.nil? || domain.empty?

  resolve_network!
  resolve_operator_wallet!
  validate_payee_source!
  auto_enable_default_gateways! if should_auto_enable_defaults?
  build_gateways_from_specs! if gateways.empty? && !gateway_specs.empty?
  validate_gateways!
  warn_operational_concerns!
  finalise_status_endpoint!
  raise ConfigurationError, "at least one route must be protected" if routes.empty?
end