Skip to content

Settings

The settings module provides type-safe configuration management using Pydantic Settings.

Overview

Configuration is loaded from multiple sources in priority order: 1. Environment variables (highest priority) 2. ./config.toml (project directory) 3. ~/.config/imbi/config.toml (user directory) 4. /etc/imbi/config.toml (system directory) 5. Built-in defaults (lowest priority)

Loading Configuration

from imbi.common import settings

# Load full configuration
config = settings.load_config()

# Access individual settings sections
postgres_config = settings.Postgres()
clickhouse_config = settings.Clickhouse()
auth_config = settings.Auth()

API Reference

load_config

load_config() -> Configuration

Load configuration from config.toml files with environment overrides.

Checks for config files in priority order: 1. ./config.toml (project root) 2. ~/.config/imbi/config.toml (user config) 3. /etc/imbi/config.toml (system config)

An environment variable overrides the same key in a config file.

Returns:

Type Description
Configuration

Configuration object with merged settings

Source code in libraries/common/src/imbi/common/settings.py
def load_config() -> Configuration:
    """Load configuration from config.toml files with environment overrides.

    Checks for config files in priority order:
    1. ./config.toml (project root)
    2. ~/.config/imbi/config.toml (user config)
    3. /etc/imbi/config.toml (system config)

    An environment variable overrides the same key in a config file.

    Returns:
        Configuration object with merged settings

    """
    return Configuration()

get_auth_settings

get_auth_settings() -> Auth

Get the singleton Auth settings instance.

This ensures the JWT secret remains stable across requests when auto-generated (i.e., when IMBI_AUTH_JWT_SECRET is not set in env).

Returns:

Type Description
Auth

The singleton Auth settings instance.

Source code in libraries/common/src/imbi/common/settings.py
def get_auth_settings() -> Auth:
    """Get the singleton Auth settings instance.

    This ensures the JWT secret remains stable across requests when
    auto-generated (i.e., when IMBI_AUTH_JWT_SECRET is not set in env).

    Returns:
        The singleton Auth settings instance.

    """
    global _auth_settings
    if _auth_settings is None:
        _auth_settings = Auth()
    return _auth_settings

get_config_settings

get_config_settings() -> ConfigSecrets

Get the singleton ConfigSecrets settings instance.

This ensures the config encryption key remains stable across requests when auto-generated (i.e., when IMBI_CONFIG_ENCRYPTION_KEY is not set in env).

Returns:

Type Description
ConfigSecrets

The singleton ConfigSecrets settings instance.

Source code in libraries/common/src/imbi/common/settings.py
def get_config_settings() -> ConfigSecrets:
    """Get the singleton ConfigSecrets settings instance.

    This ensures the config encryption key remains stable across requests
    when auto-generated (i.e., when IMBI_CONFIG_ENCRYPTION_KEY is not set in
    env).

    Returns:
        The singleton ConfigSecrets settings instance.

    """
    global _config_settings
    if _config_settings is None:
        _config_settings = ConfigSecrets()
    return _config_settings

Configuration

Bases: BaseModel

Root configuration combining all shared settings sections.

Each section reads its own config.toml section through :class:ConfigFileSettings. Config files are checked in this priority order: 1. ./config.toml (project root) 2. ~/.config/imbi/config.toml (user config) 3. /etc/imbi/config.toml (system config)

An environment variable overrides the same key in a config file.

Example config.toml: [postgres] url = "postgresql://postgres:secret@db-prod:5432/imbi"

[auth]
jwt_secret = "your-secret-here"
access_token_expire_seconds = 7200

Postgres

Bases: ConfigFileSettings

PostgreSQL connection settings.

Clickhouse

Bases: ConfigFileSettings

cluster_name class-attribute instance-attribute

cluster_name: str | None = None

Name of the ClickHouse cluster (CLICKHOUSE_CLUSTER_NAME).

When set, setup_schema() injects ON CLUSTER <cluster_name> into the CREATE DDL statements loaded from schemata.toml so the schema is created across every node of the cluster. Leave unset for a single-node deployment.

Auth

Bases: ConfigFileSettings

Authentication settings shared across Imbi services.

Contains only JWT and encryption configuration needed by any service that verifies tokens or handles encrypted data.

validate_secrets

validate_secrets() -> Auth

Enforce explicit secrets outside development; auto-generate in dev.

When ENVIRONMENT is anything other than development (the default), both IMBI_AUTH_JWT_SECRET and IMBI_AUTH_ENCRYPTION_KEY must be configured explicitly: booting with auto-generated per-process values would silently invalidate every issued token (and make every encrypted value undecryptable) on the next restart, so it is refused. In development the historical auto-generation behavior is preserved.

model_fields_set distinguishes an explicitly-supplied value (env var, config file, or kwarg) from a default, and is inspected before the dev-mode auto-generation below mutates it.

Source code in libraries/common/src/imbi/common/settings.py
@pydantic.model_validator(mode='after')
def validate_secrets(self) -> Auth:
    """Enforce explicit secrets outside development; auto-generate in dev.

    When ``ENVIRONMENT`` is anything other than ``development`` (the
    default), both ``IMBI_AUTH_JWT_SECRET`` and
    ``IMBI_AUTH_ENCRYPTION_KEY`` must be configured explicitly: booting
    with auto-generated per-process values would silently invalidate
    every issued token (and make every encrypted value undecryptable)
    on the next restart, so it is refused. In development the historical
    auto-generation behavior is preserved.

    ``model_fields_set`` distinguishes an explicitly-supplied value
    (env var, config file, or kwarg) from a default, and is inspected
    before the dev-mode auto-generation below mutates it.
    """
    if os.getenv('ENVIRONMENT', 'development').lower() != 'development':
        missing = sorted(
            name
            for name in ('jwt_secret', 'encryption_key')
            if name not in self.model_fields_set
        )
        if missing:
            raise ValueError(
                'These IMBI_AUTH_ settings must be set explicitly when '
                'ENVIRONMENT is not "development": ' + ', '.join(missing)
            )

    if self.encryption_key is None:
        from cryptography import fernet

        self.encryption_key = fernet.Fernet.generate_key().decode('ascii')
        LOGGER.warning(
            'Encryption key auto-generated. Set IMBI_AUTH_ENCRYPTION_KEY '
            'in production for stable key across restarts.'
        )
    return self

ConfigSecrets

Bases: BaseSettings

Encryption settings for persisted configuration secrets.

Provides a dedicated Fernet key, separate from :class:Auth, used to encrypt sensitive configuration values (e.g. external MCP server credentials) at rest. Keeping it distinct from the JWT/token encryption key lets the two be rotated independently.

validate_secrets

validate_secrets() -> ConfigSecrets

Enforce an explicit key outside development; auto-generate in dev.

When ENVIRONMENT is anything other than development (the default), IMBI_CONFIG_ENCRYPTION_KEY must be configured explicitly: booting with an auto-generated per-process value would silently make every encrypted configuration value undecryptable on the next restart, so it is refused. In development the historical auto-generation behavior is preserved.

model_fields_set distinguishes an explicitly-supplied value (env var, config file, or kwarg) from a default, and is inspected before the dev-mode auto-generation below mutates it.

Source code in libraries/common/src/imbi/common/settings.py
@pydantic.model_validator(mode='after')
def validate_secrets(self) -> ConfigSecrets:
    """Enforce an explicit key outside development; auto-generate in dev.

    When ``ENVIRONMENT`` is anything other than ``development`` (the
    default), ``IMBI_CONFIG_ENCRYPTION_KEY`` must be configured
    explicitly: booting with an auto-generated per-process value would
    silently make every encrypted configuration value undecryptable on
    the next restart, so it is refused. In development the historical
    auto-generation behavior is preserved.

    ``model_fields_set`` distinguishes an explicitly-supplied value
    (env var, config file, or kwarg) from a default, and is inspected
    before the dev-mode auto-generation below mutates it.
    """
    if os.getenv('ENVIRONMENT', 'development').lower() != 'development':
        if 'encryption_key' not in self.model_fields_set:
            raise ValueError(
                'IMBI_CONFIG_ENCRYPTION_KEY must be set explicitly when '
                'ENVIRONMENT is not "development"'
            )

    if self.encryption_key is None:
        from cryptography import fernet

        self.encryption_key = fernet.Fernet.generate_key().decode('ascii')
        LOGGER.warning(
            'Config encryption key auto-generated. Set '
            'IMBI_CONFIG_ENCRYPTION_KEY in production for a stable key '
            'across restarts.'
        )
    return self

SSL

Bases: ConfigFileSettings

SSL configuration settings.

configure

configure() -> None

Configure the default SSL context if cert_dir is set.

Patches ssl.create_default_context and ssl._create_default_https_context to load CA certificates from cert_dir on every call.

Source code in libraries/common/src/imbi/common/settings.py
def configure(self) -> None:
    """Configure the default SSL context if cert_dir is set.

    Patches ssl.create_default_context and
    ssl._create_default_https_context to load CA certificates from
    cert_dir on every call.
    """
    if self.cert_dir is None:
        return
    import ssl

    cert_dir = str(self.cert_dir)
    _orig = ssl.create_default_context

    def _patched(*args: object, **kwargs: object) -> ssl.SSLContext:
        ctx = _orig(*args, **kwargs)  # type: ignore[arg-type]
        ctx.load_verify_locations(capath=cert_dir)
        return ctx

    ssl.create_default_context = _patched
    ssl._create_default_https_context = _patched

Iggy

Bases: BaseSettings

Apache Iggy connection settings.

Valkey

Bases: ConfigFileSettings

Valkey connection settings.

Embeddings

Bases: ConfigFileSettings

Embedding generation settings.

Plugins

Bases: ConfigFileSettings

Plugin discovery settings.

imbi_plugins (env IMBI_PLUGINS) is an explicit list of dotted import paths (package.module:Attr) for plugin packages that cannot follow the imbi_plugin_* naming convention. The env var accepts a comma-separated string; config files supply a list. The registry unions it with the first-party and convention scans.

imbi_plugins_disabled (env IMBI_PLUGINS_DISABLED) is a list of plugin slugs to skip at load time. First-party plugins ship as imbi-plugin-* distributions and auto-load whenever they are installed; this setting turns individual plugins off without uninstalling anything.

Releases

Bases: ConfigFileSettings

Release settings.

Controls the active version_format used when validating Release.version at the endpoint boundary. The value is a runtime setting rather than a model field validator so the same model can be reused across services with different policies.

load_config_data

load_config_data() -> dict[str, typing.Any]

Load raw configuration data from config.toml files.

Checks for config files in priority order: 1. ./config.toml (project root) 2. ~/.config/imbi/config.toml (user config) 3. /etc/imbi/config.toml (system config)

Returns the merged dictionary without validation. :class:ConfigFileSettings reads its section from it.

Returns:

Type Description
dict[str, Any]

Merged configuration dictionary

Source code in libraries/common/src/imbi/common/settings.py
def load_config_data() -> dict[str, typing.Any]:
    """Load raw configuration data from config.toml files.

    Checks for config files in priority order:
    1. ./config.toml (project root)
    2. ~/.config/imbi/config.toml (user config)
    3. /etc/imbi/config.toml (system config)

    Returns the merged dictionary without validation.
    :class:`ConfigFileSettings` reads its section from it.

    Returns:
        Merged configuration dictionary

    """
    config_paths = [
        pathlib.Path.cwd() / 'config.toml',
        pathlib.Path.home() / '.config' / 'imbi' / 'config.toml',
        pathlib.Path('/etc/imbi/config.toml'),
    ]

    config_data: dict[str, typing.Any] = {}

    for config_path in config_paths:
        if config_path.exists():
            LOGGER.debug('Loading configuration from %s', config_path)
            try:
                with config_path.open('rb') as f:
                    file_data = tomllib.load(f)
                    # Merge with priority to earlier files (first found wins)
                    for key, value in file_data.items():
                        if key not in config_data:
                            config_data[key] = value
                        elif isinstance(value, dict) and isinstance(
                            config_data[key], dict
                        ):
                            # Merge nested dicts
                            config_data[key] = {**value, **config_data[key]}
            except (tomllib.TOMLDecodeError, OSError) as e:
                LOGGER.warning('Failed to load %s: %s', config_path, e)
                continue

    return config_data

base_settings_config

base_settings_config(
    *, env_prefix: str = ''
) -> pydantic_settings.SettingsConfigDict

Create a base SettingsConfigDict with common defaults.

Parameters:

Name Type Description Default
env_prefix str

Environment variable prefix

''

Returns:

Type Description
SettingsConfigDict

SettingsConfigDict with base settings

Source code in libraries/common/src/imbi/common/settings.py
def base_settings_config(
    *,
    env_prefix: str = '',
) -> pydantic_settings.SettingsConfigDict:
    """Create a base SettingsConfigDict with common defaults.

    Args:
        env_prefix: Environment variable prefix

    Returns:
        SettingsConfigDict with base settings

    """
    return pydantic_settings.SettingsConfigDict(
        case_sensitive=False,
        env_file='.env',
        env_file_encoding='utf-8',
        extra='ignore',
        env_prefix=env_prefix,
    )