2.8 KiB
Typed configuration crate and a bootstrap crate that owns all wiring
Configuration was read ad hoc from std::env in many adapters, and the two binaries each carried
hundreds of lines of adapter wiring (wire_dependencies, create_event_publisher, the worker's
handler and job assembly). ADR-0005 made composition the single root for deps, but the binaries
still named adapters themselves, and infra-wiring existed only so application could re-export an
AppConfig.
Decision
config crate. One typed config::AppConfig with sections (server, database, auth,
events, nats, storage, image_conversion, metadata, federation, wrapup), loaded once by
the binary via config::load(). Layering, lowest to highest precedence:
- built-in defaults;
config.toml, or the file named byCONFIG_FILE(a named file that is missing is an error);- environment variables, using the legacy names (
DATABASE_URL,PORT,JWT_SECRET, ...) with no prefix. Every variable that ever worked keeps its name, meaning and default.
A malformed value fails startup with an error naming the variable (PORT=abc). An empty or
whitespace-only value is treated as unset, so the default applies (or None for optional fields)
rather than being an error. Adapters take their section (or a typed config built from it), never
raw environment strings.
bootstrap crate. Replaces composition and infra-wiring. It is the only crate that names
concrete adapters. It exposes build_server(&AppConfig) -> ServerApp { router, addr },
build_worker(&AppConfig) -> WorkerApp { service }, build_server_on(&AppConfig, SqlitePool) (the
same router on a caller-provided pool, for tests) and init_tracing. DbPool and
EventBusBackend live here. application keeps a small application-owned AppConfig /
WrapUpConfig built from the config sections, so use-case deps structs and their test fixtures
are unchanged, and application no longer pulls sqlx or anyhow.
Binaries are thin. server and worker do dotenv, config::load, init_tracing, one
bootstrap::build_* call and axum::serve / service.run. Their [dependencies] are limited to
bootstrap, config, tokio, axum, anyhow, dotenvy and tracing; make check-binaries-are-thin enforces it. The federation and nats features forward to bootstrap.
Consequences
- Adding or changing an adapter touches
bootstraponly; the binaries do not change. - The wiring is reachable from tests (
build_server_on) without a process or a port. check-federation-off-socialnow inspectscrates/bootstrap/src/federation.rs: federation-off must still wireLocalSocialService, never social noops (ADR-0009).- Operators can move settings into
config.tomlincrementally; existing.envfiles keep working.