Skip to content
Menu

Documentation

Loader Configuration

The module loader decides which modules a process loads and how it reacts to load failures. It is driven entirely by settings, so the same image becomes a monolith or a single-module service by configuration alone.

Settings

Setting Meaning
SANDLOOM_MODULES modules to load explicitly (JSON list of module names or import paths)
MODULE_LOAD_ENTRYPOINTS also load every installed module package (the sandloom.modules entry-point group)
MODULE_WHITELIST allow only listed modules
MODULE_BLACKLIST skip listed modules
MODULE_REQUIRED modules whose load failure aborts startup
MODULE_FAIL_FAST raise on first required failure vs aggregate at the end

See the Configuration Reference for defaults.

Explicit list

SANDLOOM_MODULES='["identity", "tenancy", "billing"]'

create_app() reads SANDLOOM_MODULES, imports each module, and calls its register(core). Only the listed modules' routes, event schemas, and services exist in that process. An entry is a module name (identity, the sandloom.modules entry-point name of an installed module package) or an import path (sandloom_identity, myapp.modules.tickets); MODULE_REQUIRED, the whitelist and the blacklist accept the same spellings.

sandloom.main:app, the framework's own composition root, composes every installed module package (sorted by name) when SANDLOOM_MODULES is unset: with sandloom[standard] installed that is identity, tenancy and billing. The kernel names no module.

An application generated by sandloom new needs no list: create_app loads every module of the app's modules package, so sandloom add module <name> is all it takes to add one. Set SANDLOOM_MODULES only to select a subset (a single-module service in the microservices profile, for example).

Module packages

A distribution ships a module by registering it in the sandloom.modules entry-point group. The entry-point name is the module name; the value is the import path:

# in the module package's pyproject.toml
[project.entry-points."sandloom.modules"]
tickets = "acme_tickets"

Install it and name it in SANDLOOM_MODULES ("tickets"), or load every installed module package:

MODULE_LOAD_ENTRYPOINTS=true

A requested module name must have one provider. Two installed packages registering the same name, or an installed package and an app-local module with the same name (myapp.modules.tickets next to acme_tickets), are refused before anything loads (SANDLOOM-B020): uninstall the package or remove (rename) the local module. See Module packages.

MODULE_FS_SCAN no longer exists (it scanned sandloom.*, which holds no module); setting it to true fails startup.

Required modules and fail-fast

A module is required unless it says otherwise in its define_module call:

from sandloom.sdk import define_module

register = define_module(__name__, version="1.0.0")                 # required
# register = define_module(__name__, version="1.0.0", optional=True)  # may fail to load
  • A required module is only enforced when it is actually present in SANDLOOM_MODULES, so a billing-only service does not need identity co-deployed.
  • With MODULE_FAIL_FAST=true the loader re-raises on the first required failure; otherwise it aggregates and raises ModuleLoadError at the end.

Migration chain keys

Each loaded module with a migrations/ directory owns a chain keyed by its module name (the entry-point name of a module package, else the last dotted segment of its import path), or by ModuleDescriptor.migrations_chain. Two loaded modules with the same key (app_a.modules.notes and app_b.modules.notes) are refused instead of silently sharing alembic_version_notes — set migrations_chain on one of them. See Migrations Model.

Diagnostics

Duplicates and whitelist/blacklist skips are recorded in the load summary, and the configured modules are reported at /diagnostics/* (requires INTERNAL_API_TOKEN).