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

#initialize ⇒ Configuration

Returns a new instance of Configuration.



71
72
73
74
75
76
77
78
79
80
# File 'lib/x402/configuration.rb', line 71

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_key ⇒ Object

Returns the value of attribute arc_api_key.



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

def arc_api_key
  @arc_api_key
end

#arc_client ⇒ Object

Returns the value of attribute arc_client.



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

def arc_client
  @arc_client
end

#arc_url ⇒ Object

Returns the value of attribute arc_url.



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

def arc_url
  @arc_url
end

#domain ⇒ Object

Returns the value of attribute domain.



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

def domain
  @domain
end

#exchange_rate_provider ⇒ Object

Returns the value of attribute exchange_rate_provider.



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

def exchange_rate_provider
  @exchange_rate_provider
end

#gateway_specs ⇒ Object (readonly)

Returns the value of attribute gateway_specs.



67
68
69
# File 'lib/x402/configuration.rb', line 67

def gateway_specs
  @gateway_specs
end

#gateways ⇒ Object

Returns the value of attribute gateways.



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

def gateways
  @gateways
end

#logger ⇒ Object

Returns the value of attribute logger.



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

def logger
  @logger
end

#network ⇒ Object

Returns the value of attribute network.



67
68
69
# File 'lib/x402/configuration.rb', line 67

def network
  @network
end

#operator_wallet_url ⇒ Object

Returns the value of attribute operator_wallet_url.



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

def operator_wallet_url
  @operator_wallet_url
end

#payee_locking_script_hex ⇒ Object

Returns the value of attribute payee_locking_script_hex.



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

def payee_locking_script_hex
  @payee_locking_script_hex
end

#routes ⇒ Object (readonly)

Returns the value of attribute routes.



67
68
69
# File 'lib/x402/configuration.rb', line 67

def routes
  @routes
end

#server_wif ⇒ Object

Returns the value of attribute server_wif.



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

def server_wif
  @server_wif
end

#status_endpoint_path ⇒ Object

Returns the value of attribute status_endpoint_path.



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

def status_endpoint_path
  @status_endpoint_path
end

#status_endpoint_token ⇒ Object

Returns the value of attribute status_endpoint_token.



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

def status_endpoint_token
  @status_endpoint_token
end

#wallet ⇒ Object

Returns the value of attribute wallet.



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

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:



127
128
129
130
131
132
# File 'lib/x402/configuration.rb', line 127

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_endpoint ⇒ void

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.



93
94
95
# File 'lib/x402/configuration.rb', line 93

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:



190
191
192
193
194
195
# File 'lib/x402/configuration.rb', line 190

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.



182
183
184
185
# File 'lib/x402/configuration.rb', line 182

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_client ⇒ BSV::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)


140
141
142
143
144
# File 'lib/x402/configuration.rb', line 140

def shared_arc_client
  return @arc_client if @arc_client

  @shared_arc_client ||= build_arc_client
end

#shared_wallet ⇒ BSV::Wallet::Client, ...

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::Client+), or a +RemoteWallet+
 auto-constructed from +operator_wallet_url+
2. A +Client+ built from +server_wif+ (backwards compat)
3. +nil+ if neither is set

Returns:



157
158
159
160
161
162
# File 'lib/x402/configuration.rb', line 157

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



98
99
100
# File 'lib/x402/configuration.rb', line 98

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:



204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/x402/configuration.rb', line 204

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