Module jmcore.settings
Unified settings management for JoinMarket components.
This module provides a centralized configuration system using pydantic-settings that supports: 1. TOML configuration file (~/.joinmarket-ng/config.toml) 2. Environment variables 3. CLI arguments (via typer, handled by components)
Priority (highest to lowest): 1. CLI arguments 2. Environment variables 3. Config file 4. Default values
The config file is auto-generated on first run with all settings commented out, allowing users to selectively override only the settings they want to change. This approach facilitates software updates since unchanged defaults can be updated without user intervention.
Usage
from jmcore.settings import get_settings, JoinMarketSettings
Get settings (loads from all sources with proper priority)
settings = get_settings()
Access common settings
print(settings.tor.socks_host) print(settings.bitcoin.rpc_url)
Environment Variable Naming: - Use uppercase with double underscore for nested settings - Examples: TOR__SOCKS_HOST, BITCOIN__RPC_URL, MAKER__MIN_SIZE - Maps to TOML sections: TOR__SOCKS_HOST -> [tor] socks_host
Functions
def ensure_config_file(data_dir: Path | None = None) ‑> pathlib.Path-
Expand source code
def ensure_config_file(data_dir: Path | None = None) -> Path: """ Ensure the config file exists, creating a template if it doesn't. Args: data_dir: Optional data directory path. Uses default if not provided. Returns: Path to the config file. """ if data_dir is None: data_dir = get_default_data_dir() config_path = data_dir / "config.toml" if not config_path.exists(): logger.info(f"Creating config file template at {config_path}") data_dir.mkdir(parents=True, exist_ok=True) config_path.write_text(generate_config_template()) return config_pathEnsure the config file exists, creating a template if it doesn't.
Args
data_dir- Optional data directory path. Uses default if not provided.
Returns
Path to the config file.
def generate_config_template() ‑> str-
Expand source code
def generate_config_template() -> str: """ Generate a config file template with all settings commented out. This allows users to see all available settings with their defaults and descriptions, while only uncommenting what they want to change. """ lines: list[str] = [] lines.append("# JoinMarket NG Configuration") lines.append("#") lines.append("# This file contains all available settings with their default values.") lines.append("# Settings are commented out by default - uncomment to override.") lines.append("#") lines.append("# Priority (highest to lowest):") lines.append("# 1. CLI arguments") lines.append("# 2. Environment variables") lines.append("# 3. This config file") lines.append("# 4. Built-in defaults") lines.append("#") lines.append("# Environment variables use uppercase with double underscore for nesting:") lines.append("# TOR__SOCKS_HOST=127.0.0.1") lines.append("# BITCOIN__RPC_URL=http://localhost:8332") lines.append("#") lines.append("") # Generate sections for each nested model def add_section(title: str, model_cls: type[BaseModel], prefix: str = "") -> None: lines.append(f"# {'=' * 60}") lines.append(f"# {title}") lines.append(f"# {'=' * 60}") lines.append(f"[{prefix}]" if prefix else "") lines.append("") for field_name, field_info in model_cls.model_fields.items(): # Get description desc = field_info.description or "" if desc: lines.append(f"# {desc}") # Get default value default = field_info.default factory = field_info.default_factory if factory is not None: # default_factory can be Callable[[], Any] or Callable[[dict], Any] # We call with no args for the common case try: default = factory() # type: ignore[call-arg] except TypeError: default = factory({}) # type: ignore[call-arg] # Format the value for TOML if isinstance(default, bool): value_str = str(default).lower() elif isinstance(default, str): value_str = f'"{default}"' elif isinstance(default, list): # For directory_servers, show example from defaults if field_name == "directory_servers" and prefix == "network_config": lines.append("# directory_servers = [") for server in DEFAULT_DIRECTORY_SERVERS["mainnet"]: lines.append(f'# "{server}",') lines.append("# ]") lines.append("") continue value_str = "[]" if not default else str(default).replace("'", '"') elif isinstance(default, SecretStr): value_str = '""' elif default is None: # Skip None values with a comment lines.append(f"# {field_name} = ") lines.append("") continue elif hasattr(default, "value"): # Enum - use string value value_str = f'"{default.value}"' else: value_str = str(default) lines.append(f"# {field_name} = {value_str}") lines.append("") # Data directory (top-level) lines.append("# Data directory for JoinMarket files") lines.append("# Defaults to ~/.joinmarket-ng or $JOINMARKET_DATA_DIR") lines.append("# data_dir = ") lines.append("") # Add all sections add_section("Tor Settings", TorSettings, "tor") add_section("Bitcoin Backend Settings", BitcoinSettings, "bitcoin") add_section("Network Settings", NetworkSettings, "network_config") add_section("Wallet Settings", WalletSettings, "wallet") add_section("Notification Settings", NotificationSettings, "notifications") add_section("Logging Settings", LoggingSettings, "logging") add_section("Maker Settings", MakerSettings, "maker") add_section("Taker Settings", TakerSettings, "taker") add_section("Directory Server Settings", DirectoryServerSettings, "directory_server") add_section("Orderbook Watcher Settings", OrderbookWatcherSettings, "orderbook_watcher") return "\n".join(lines)Generate a config file template with all settings commented out.
This allows users to see all available settings with their defaults and descriptions, while only uncommenting what they want to change.
def get_config_path() ‑> pathlib.Path-
Expand source code
def get_config_path() -> Path: """Get the path to the config file.""" data_dir_env = os.environ.get("JOINMARKET_DATA_DIR") data_dir = Path(data_dir_env) if data_dir_env else Path.home() / ".joinmarket-ng" return data_dir / "config.toml"Get the path to the config file.
def get_settings(**overrides: Any) ‑> JoinMarketSettings-
Expand source code
def get_settings(**overrides: Any) -> JoinMarketSettings: """ Get the JoinMarket settings instance. On first call, loads settings from all sources. Subsequent calls return the cached instance unless reset_settings() is called. Args: **overrides: Optional settings overrides (highest priority) Returns: JoinMarketSettings instance """ global _settings if _settings is None or overrides: _settings = JoinMarketSettings(**overrides) return _settingsGet the JoinMarket settings instance.
On first call, loads settings from all sources. Subsequent calls return the cached instance unless reset_settings() is called.
Args
**overrides- Optional settings overrides (highest priority)
Returns
JoinMarketSettings instance
def reset_settings() ‑> None-
Expand source code
def reset_settings() -> None: """Reset the global settings instance (useful for testing).""" global _settings _settings = NoneReset the global settings instance (useful for testing).
Classes
class BitcoinSettings (**data: Any)-
Expand source code
class BitcoinSettings(BaseModel): """Bitcoin backend configuration.""" backend_type: str = Field( default="descriptor_wallet", description="Backend type: scantxoutset, descriptor_wallet, or neutrino", ) rpc_url: str = Field( default="http://127.0.0.1:8332", description="Bitcoin Core RPC URL", ) rpc_user: str = Field( default="", description="Bitcoin Core RPC username", ) rpc_password: SecretStr = Field( default=SecretStr(""), description="Bitcoin Core RPC password", ) neutrino_url: str = Field( default="http://127.0.0.1:8334", description="Neutrino REST API URL (for neutrino backend)", )Bitcoin backend configuration.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var backend_type : str-
The type of the None singleton.
var model_config-
The type of the None singleton.
var neutrino_url : str-
The type of the None singleton.
var rpc_password : pydantic.types.SecretStr-
The type of the None singleton.
var rpc_url : str-
The type of the None singleton.
var rpc_user : str-
The type of the None singleton.
class DirectoryServerSettings (**data: Any)-
Expand source code
class DirectoryServerSettings(BaseModel): """Directory server specific settings.""" host: str = Field( default="127.0.0.1", description="Host address to bind to", ) port: int = Field( default=5222, ge=0, le=65535, description="Port to listen on (0 = let OS assign)", ) max_peers: int = Field( default=10000, ge=1, description="Maximum number of connected peers", ) max_message_size: int = Field( default=2097152, ge=1024, description="Maximum message size in bytes (2MB default)", ) max_line_length: int = Field( default=65536, ge=1024, description="Maximum JSON-line message length (64KB default)", ) max_json_nesting_depth: int = Field( default=10, ge=1, description="Maximum nesting depth for JSON parsing", ) message_rate_limit: int = Field( default=500, ge=1, description="Messages per second (sustained)", ) message_burst_limit: int = Field( default=1000, ge=1, description="Maximum burst size", ) rate_limit_disconnect_threshold: int = Field( default=0, ge=0, description="Disconnect after N rate limit violations (0 = never disconnect)", ) broadcast_batch_size: int = Field( default=50, ge=1, description="Batch size for concurrent broadcasts", ) health_check_host: str = Field( default="127.0.0.1", description="Host for health check endpoint", ) health_check_port: int = Field( default=8080, ge=0, le=65535, description="Port for health check endpoint (0 = let OS assign)", ) motd: str = Field( default="JoinMarket NG Directory Server https://github.com/m0wer/joinmarket-ng/", description="Message of the day sent to clients", )Directory server specific settings.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var broadcast_batch_size : int-
The type of the None singleton.
var health_check_host : str-
The type of the None singleton.
var health_check_port : int-
The type of the None singleton.
var host : str-
The type of the None singleton.
var max_json_nesting_depth : int-
The type of the None singleton.
var max_line_length : int-
The type of the None singleton.
var max_message_size : int-
The type of the None singleton.
var max_peers : int-
The type of the None singleton.
var message_burst_limit : int-
The type of the None singleton.
var message_rate_limit : int-
The type of the None singleton.
var model_config-
The type of the None singleton.
var motd : str-
The type of the None singleton.
var port : int-
The type of the None singleton.
var rate_limit_disconnect_threshold : int-
The type of the None singleton.
class JoinMarketSettings (**values: Any)-
Expand source code
class JoinMarketSettings(BaseSettings): """ Main JoinMarket settings class. Loads configuration from multiple sources with the following priority: 1. CLI arguments (not handled here, passed to component __init__) 2. Environment variables 3. TOML config file (~/.joinmarket-ng/config.toml) 4. Default values """ model_config = SettingsConfigDict( env_prefix="", # No prefix by default, use env_nested_delimiter for nested env_nested_delimiter="__", case_sensitive=False, extra="ignore", # Ignore unknown fields (for forward compatibility) ) # Marker for config file path discovery _config_file_path: ClassVar[Path | None] = None # Core settings data_dir: Path | None = Field( default=None, description="Data directory (defaults to ~/.joinmarket-ng)", ) # Nested settings groups tor: TorSettings = Field(default_factory=TorSettings) bitcoin: BitcoinSettings = Field(default_factory=BitcoinSettings) network_config: NetworkSettings = Field(default_factory=NetworkSettings) wallet: WalletSettings = Field(default_factory=WalletSettings) notifications: NotificationSettings = Field(default_factory=NotificationSettings) logging: LoggingSettings = Field(default_factory=LoggingSettings) # Component-specific settings maker: MakerSettings = Field(default_factory=MakerSettings) taker: TakerSettings = Field(default_factory=TakerSettings) directory_server: DirectoryServerSettings = Field(default_factory=DirectoryServerSettings) orderbook_watcher: OrderbookWatcherSettings = Field(default_factory=OrderbookWatcherSettings) @classmethod def settings_customise_sources( cls, settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource, ) -> tuple[PydanticBaseSettingsSource, ...]: """ Customize settings sources and their priority. Priority (highest to lowest): 1. init_settings (CLI arguments passed to constructor) 2. env_settings (environment variables with __ delimiter) 3. toml_settings (config.toml file) 4. defaults (in field definitions) """ toml_source = TomlConfigSettingsSource(settings_cls) return ( init_settings, env_settings, toml_source, ) def get_data_dir(self) -> Path: """Get the data directory, using default if not set.""" if self.data_dir is not None: return self.data_dir return get_default_data_dir() def get_directory_servers(self) -> list[str]: """Get directory servers, using network defaults if not set.""" if self.network_config.directory_servers: return self.network_config.directory_servers network_name = self.network_config.network.value return DEFAULT_DIRECTORY_SERVERS.get(network_name, [])Main JoinMarket settings class.
Loads configuration from multiple sources with the following priority: 1. CLI arguments (not handled here, passed to component init) 2. Environment variables 3. TOML config file (~/.joinmarket-ng/config.toml) 4. Default values
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic_settings.main.BaseSettings
- pydantic.main.BaseModel
Class variables
var bitcoin : BitcoinSettings-
The type of the None singleton.
var data_dir : pathlib.Path | None-
The type of the None singleton.
var directory_server : DirectoryServerSettings-
The type of the None singleton.
var logging : LoggingSettings-
The type of the None singleton.
var maker : MakerSettings-
The type of the None singleton.
var model_config : ClassVar[pydantic_settings.main.SettingsConfigDict]-
The type of the None singleton.
var network_config : NetworkSettings-
The type of the None singleton.
var notifications : NotificationSettings-
The type of the None singleton.
var orderbook_watcher : OrderbookWatcherSettings-
The type of the None singleton.
var taker : TakerSettings-
The type of the None singleton.
var tor : TorSettings-
The type of the None singleton.
var wallet : WalletSettings-
The type of the None singleton.
Static methods
def settings_customise_sources(settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource) ‑> tuple[pydantic_settings.sources.base.PydanticBaseSettingsSource, ...]-
Customize settings sources and their priority.
Priority (highest to lowest): 1. init_settings (CLI arguments passed to constructor) 2. env_settings (environment variables with __ delimiter) 3. toml_settings (config.toml file) 4. defaults (in field definitions)
Methods
def get_data_dir(self) ‑> pathlib.Path-
Expand source code
def get_data_dir(self) -> Path: """Get the data directory, using default if not set.""" if self.data_dir is not None: return self.data_dir return get_default_data_dir()Get the data directory, using default if not set.
def get_directory_servers(self) ‑> list[str]-
Expand source code
def get_directory_servers(self) -> list[str]: """Get directory servers, using network defaults if not set.""" if self.network_config.directory_servers: return self.network_config.directory_servers network_name = self.network_config.network.value return DEFAULT_DIRECTORY_SERVERS.get(network_name, [])Get directory servers, using network defaults if not set.
class LoggingSettings (**data: Any)-
Expand source code
class LoggingSettings(BaseModel): """Logging configuration.""" level: str = Field( default="INFO", description="Log level: TRACE, DEBUG, INFO, WARNING, ERROR", ) sensitive: bool = Field( default=False, description="Enable sensitive logging (mnemonics, keys)", )Logging configuration.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var level : str-
The type of the None singleton.
var model_config-
The type of the None singleton.
var sensitive : bool-
The type of the None singleton.
class MakerSettings (**data: Any)-
Expand source code
class MakerSettings(BaseModel): """Maker-specific settings.""" min_size: int = Field( default=100000, ge=0, description="Minimum CoinJoin amount in satoshis", ) offer_type: str = Field( default="sw0reloffer", description="Offer type: sw0reloffer (relative) or sw0absoffer (absolute)", ) cj_fee_relative: str = Field( default="0.001", description="Relative CoinJoin fee (0.001 = 0.1%)", ) cj_fee_absolute: int = Field( default=500, ge=0, description="Absolute CoinJoin fee in satoshis", ) tx_fee_contribution: int = Field( default=0, ge=0, description="Transaction fee contribution in satoshis", ) min_confirmations: int = Field( default=1, ge=0, description="Minimum confirmations for UTXOs", ) merge_algorithm: str = Field( default="default", description="UTXO selection: default, gradual, greedy, random", ) session_timeout_sec: int = Field( default=300, ge=60, description="Maximum time for a CoinJoin session", ) pending_tx_timeout_min: int = Field( default=60, ge=10, le=1440, description="Minutes before marking unbroadcast CoinJoins as failed", ) rescan_interval_sec: int = Field( default=600, ge=60, description="Interval for periodic wallet rescans", ) # Hidden service settings onion_serving_host: str = Field( default="127.0.0.1", description="Bind address for incoming connections", ) onion_serving_port: int = Field( default=5222, ge=0, le=65535, description="Port for incoming onion connections", ) # Rate limiting message_rate_limit: int = Field( default=10, ge=1, description="Messages per second per peer (sustained)", ) message_burst_limit: int = Field( default=100, ge=1, description="Maximum burst messages per peer", ) @field_validator("cj_fee_relative", mode="before") @classmethod def normalize_cj_fee_relative(cls, v: str | float | int) -> str: """ Normalize cj_fee_relative to avoid scientific notation. Pydantic may coerce float values (from env vars, TOML, or JSON) to strings, which can result in scientific notation for small values (e.g., 1e-05). The JoinMarket protocol expects decimal notation (e.g., 0.00001). """ if isinstance(v, (int, float)): # Use Decimal to preserve precision and avoid scientific notation return format(Decimal(str(v)), "f") # Already a string - check if it contains scientific notation if "e" in v.lower(): try: return format(Decimal(v), "f") except InvalidOperation: pass # Let pydantic handle the validation error return vMaker-specific settings.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var cj_fee_absolute : int-
The type of the None singleton.
var cj_fee_relative : str-
The type of the None singleton.
var merge_algorithm : str-
The type of the None singleton.
var message_burst_limit : int-
The type of the None singleton.
var message_rate_limit : int-
The type of the None singleton.
var min_confirmations : int-
The type of the None singleton.
var min_size : int-
The type of the None singleton.
var model_config-
The type of the None singleton.
var offer_type : str-
The type of the None singleton.
var onion_serving_host : str-
The type of the None singleton.
var onion_serving_port : int-
The type of the None singleton.
var pending_tx_timeout_min : int-
The type of the None singleton.
var rescan_interval_sec : int-
The type of the None singleton.
var session_timeout_sec : int-
The type of the None singleton.
var tx_fee_contribution : int-
The type of the None singleton.
Static methods
def normalize_cj_fee_relative(v: str | float | int) ‑> str-
Normalize cj_fee_relative to avoid scientific notation.
Pydantic may coerce float values (from env vars, TOML, or JSON) to strings, which can result in scientific notation for small values (e.g., 1e-05). The JoinMarket protocol expects decimal notation (e.g., 0.00001).
class NetworkSettings (**data: Any)-
Expand source code
class NetworkSettings(BaseModel): """Network configuration.""" network: NetworkType = Field( default=NetworkType.MAINNET, description="JoinMarket protocol network (mainnet, testnet, signet, regtest)", ) bitcoin_network: NetworkType | None = Field( default=None, description="Bitcoin network for address generation (defaults to network)", ) directory_servers: list[str] = Field( default_factory=list, description="Directory server addresses (host:port). Uses defaults if empty.", )Network configuration.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var bitcoin_network : NetworkType | None-
The type of the None singleton.
var directory_servers : list[str]-
The type of the None singleton.
var model_config-
The type of the None singleton.
var network : NetworkType-
The type of the None singleton.
class NotificationSettings (**data: Any)-
Expand source code
class NotificationSettings(BaseModel): """Notification system configuration.""" enabled: bool = Field( default=False, description="Enable notifications (requires urls to be set)", ) urls: list[str] = Field( default_factory=list, description='Apprise notification URLs (e.g., ["tgram://bottoken/ChatID", "gotify://hostname/token"])', ) title_prefix: str = Field( default="JoinMarket NG", description="Prefix for notification titles", ) component_name: str = Field( default="", description="Component name in notification titles (e.g., 'Maker', 'Taker'). " "Usually set programmatically by each component.", ) include_amounts: bool = Field( default=True, description="Include amounts in notifications", ) include_txids: bool = Field( default=False, description="Include transaction IDs in notifications (privacy risk)", ) include_nick: bool = Field( default=True, description="Include peer nicks in notifications", ) use_tor: bool = Field( default=True, description="Route notifications through Tor SOCKS proxy", ) # Event type toggles notify_fill: bool = Field(default=True, description="Notify on !fill requests") notify_rejection: bool = Field(default=True, description="Notify on rejections") notify_signing: bool = Field(default=True, description="Notify on transaction signing") notify_mempool: bool = Field(default=True, description="Notify on mempool detection") notify_confirmed: bool = Field(default=True, description="Notify on confirmation") notify_nick_change: bool = Field(default=True, description="Notify on nick change") notify_disconnect: bool = Field(default=True, description="Notify on directory disconnect") notify_coinjoin_start: bool = Field(default=True, description="Notify on CoinJoin start") notify_coinjoin_complete: bool = Field(default=True, description="Notify on CoinJoin complete") notify_coinjoin_failed: bool = Field(default=True, description="Notify on CoinJoin failure") notify_peer_events: bool = Field(default=False, description="Notify on peer connect/disconnect") notify_rate_limit: bool = Field(default=True, description="Notify on rate limit bans") notify_startup: bool = Field(default=True, description="Notify on component startup")Notification system configuration.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var component_name : str-
The type of the None singleton.
var enabled : bool-
The type of the None singleton.
var include_amounts : bool-
The type of the None singleton.
var include_nick : bool-
The type of the None singleton.
var include_txids : bool-
The type of the None singleton.
var model_config-
The type of the None singleton.
var notify_coinjoin_complete : bool-
The type of the None singleton.
var notify_coinjoin_failed : bool-
The type of the None singleton.
var notify_coinjoin_start : bool-
The type of the None singleton.
var notify_confirmed : bool-
The type of the None singleton.
var notify_disconnect : bool-
The type of the None singleton.
var notify_fill : bool-
The type of the None singleton.
var notify_mempool : bool-
The type of the None singleton.
var notify_nick_change : bool-
The type of the None singleton.
var notify_peer_events : bool-
The type of the None singleton.
var notify_rate_limit : bool-
The type of the None singleton.
var notify_rejection : bool-
The type of the None singleton.
var notify_signing : bool-
The type of the None singleton.
var notify_startup : bool-
The type of the None singleton.
var title_prefix : str-
The type of the None singleton.
var urls : list[str]-
The type of the None singleton.
var use_tor : bool-
The type of the None singleton.
class OrderbookWatcherSettings (**data: Any)-
Expand source code
class OrderbookWatcherSettings(BaseModel): """Orderbook watcher specific settings.""" http_host: str = Field( default="0.0.0.0", description="HTTP server bind address", ) http_port: int = Field( default=8000, ge=1, le=65535, description="HTTP server port", ) update_interval: int = Field( default=60, ge=10, description="Update interval in seconds", ) mempool_api_url: str = Field( default="http://mempopwcaqoi7z5xj5zplfdwk5bgzyl3hemx725d4a3agado6xtk3kqd.onion/api", description="Mempool API URL for transaction lookups", ) mempool_web_url: str | None = Field( default="https://mempool.sgn.space", description="Mempool web URL for human-readable links", ) uptime_grace_period: int = Field( default=60, ge=0, description="Grace period before tracking uptime", ) max_message_size: int = Field( default=2097152, ge=1024, description="Maximum message size in bytes (2MB default)", ) connection_timeout: float = Field( default=30.0, gt=0.0, description="Connection timeout in seconds", )Orderbook watcher specific settings.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var connection_timeout : float-
The type of the None singleton.
var http_host : str-
The type of the None singleton.
var http_port : int-
The type of the None singleton.
var max_message_size : int-
The type of the None singleton.
var mempool_api_url : str-
The type of the None singleton.
var mempool_web_url : str | None-
The type of the None singleton.
var model_config-
The type of the None singleton.
var update_interval : int-
The type of the None singleton.
var uptime_grace_period : int-
The type of the None singleton.
class TakerSettings (**data: Any)-
Expand source code
class TakerSettings(BaseModel): """Taker-specific settings.""" counterparty_count: int = Field( default=10, ge=1, le=20, description="Number of makers to select for CoinJoin", ) max_cj_fee_abs: int = Field( default=500, ge=0, description="Maximum absolute CoinJoin fee in satoshis", ) max_cj_fee_rel: str = Field( default="0.001", description="Maximum relative CoinJoin fee (0.001 = 0.1%)", ) tx_fee_factor: float = Field( default=3.0, ge=1.0, description="Multiply estimated fee by this factor", ) fee_block_target: int | None = Field( default=None, ge=1, le=1008, description="Target blocks for fee estimation", ) bondless_makers_allowance: float = Field( default=0.0, ge=0.0, le=1.0, description="Fraction of time to choose makers randomly", ) bond_value_exponent: float = Field( default=1.3, gt=0.0, description="Exponent for fidelity bond value calculation", ) bondless_require_zero_fee: bool = Field( default=True, description="Require zero absolute fee for bondless maker spots", ) maker_timeout_sec: int = Field( default=60, ge=10, description="Timeout for maker responses", ) order_wait_time: float = Field( default=120.0, ge=1.0, description=( "Seconds to wait for orderbook responses. Empirical testing shows 95th " "percentile response time over Tor is ~101s. Default 120s (with 20% buffer) " "captures ~95% of offers." ), ) tx_broadcast: str = Field( default="random-peer", description="Broadcast policy: self, random-peer, multiple-peers, not-self", ) broadcast_peer_count: int = Field( default=3, ge=1, description="Number of peers for multiple-peers broadcast", ) minimum_makers: int = Field( default=1, ge=1, description="Minimum number of makers required", ) rescan_interval_sec: int = Field( default=600, ge=60, description="Interval for periodic wallet rescans", )Taker-specific settings.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var bond_value_exponent : float-
The type of the None singleton.
var bondless_makers_allowance : float-
The type of the None singleton.
var bondless_require_zero_fee : bool-
The type of the None singleton.
var broadcast_peer_count : int-
The type of the None singleton.
var counterparty_count : int-
The type of the None singleton.
var fee_block_target : int | None-
The type of the None singleton.
var maker_timeout_sec : int-
The type of the None singleton.
var max_cj_fee_abs : int-
The type of the None singleton.
var max_cj_fee_rel : str-
The type of the None singleton.
var minimum_makers : int-
The type of the None singleton.
var model_config-
The type of the None singleton.
var order_wait_time : float-
The type of the None singleton.
var rescan_interval_sec : int-
The type of the None singleton.
var tx_broadcast : str-
The type of the None singleton.
var tx_fee_factor : float-
The type of the None singleton.
class TorSettings (**data: Any)-
Expand source code
class TorSettings(BaseModel): """Tor proxy and control port configuration.""" # SOCKS proxy settings socks_host: str = Field( default="127.0.0.1", description="Tor SOCKS5 proxy host", ) socks_port: int = Field( default=9050, ge=1, le=65535, description="Tor SOCKS5 proxy port", ) # Control port settings control_enabled: bool = Field( default=True, description="Enable Tor control port integration for ephemeral hidden services", ) control_host: str = Field( default="127.0.0.1", description="Tor control port host", ) control_port: int = Field( default=9051, ge=1, le=65535, description="Tor control port", ) cookie_path: str | None = Field( default=None, description="Path to Tor cookie auth file", ) password: SecretStr | None = Field( default=None, description="Tor control port password (use cookie auth instead if possible)", ) # Hidden service target (for makers) target_host: str = Field( default="127.0.0.1", description="Target host for Tor hidden service (usually container name in Docker)", )Tor proxy and control port configuration.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var control_enabled : bool-
The type of the None singleton.
var control_host : str-
The type of the None singleton.
var control_port : int-
The type of the None singleton.
-
The type of the None singleton.
var model_config-
The type of the None singleton.
var password : pydantic.types.SecretStr | None-
The type of the None singleton.
var socks_host : str-
The type of the None singleton.
var socks_port : int-
The type of the None singleton.
var target_host : str-
The type of the None singleton.
class WalletSettings (**data: Any)-
Expand source code
class WalletSettings(BaseModel): """Wallet configuration.""" mixdepth_count: int = Field( default=5, ge=1, le=10, description="Number of mixdepths (privacy compartments)", ) gap_limit: int = Field( default=20, ge=6, description="BIP44 gap limit for address scanning", ) dust_threshold: int = Field( default=27300, ge=0, description="Dust threshold in satoshis", ) smart_scan: bool = Field( default=True, description="Use smart scan for fast startup", ) background_full_rescan: bool = Field( default=True, description="Run full blockchain rescan in background", ) scan_lookback_blocks: int = Field( default=52560, ge=0, description="Blocks to look back for smart scan (~1 year default)", ) scan_start_height: int | None = Field( default=None, ge=0, description="Explicit start height for initial scan (overrides scan_lookback_blocks if set)", ) default_fee_block_target: int = Field( default=3, ge=1, le=1008, description="Default block target for fee estimation in wallet transactions", ) mnemonic_file: str | None = Field( default=None, description="Default path to mnemonic file", ) mnemonic_password: SecretStr | None = Field( default=None, description="Password for encrypted mnemonic file", ) bip39_passphrase: SecretStr | None = Field( default=None, description="BIP39 passphrase (13th/25th word). For security, prefer BIP39_PASSPHRASE env var.", )Wallet configuration.
Create a new model by parsing and validating input data from keyword arguments.
Raises [
ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.selfis explicitly positional-only to allowselfas a field name.Ancestors
- pydantic.main.BaseModel
Class variables
var background_full_rescan : bool-
The type of the None singleton.
var bip39_passphrase : pydantic.types.SecretStr | None-
The type of the None singleton.
var default_fee_block_target : int-
The type of the None singleton.
var dust_threshold : int-
The type of the None singleton.
var gap_limit : int-
The type of the None singleton.
var mixdepth_count : int-
The type of the None singleton.
var mnemonic_file : str | None-
The type of the None singleton.
var mnemonic_password : pydantic.types.SecretStr | None-
The type of the None singleton.
var model_config-
The type of the None singleton.
var scan_lookback_blocks : int-
The type of the None singleton.
var scan_start_height : int | None-
The type of the None singleton.
var smart_scan : bool-
The type of the None singleton.