Skip to main content

Bootstrap & Configuration Reference

Source code: src/qtg/infrastructure/bootstrap.py, src/qtg/pro_loader.py, config.py, main.py
Defines the qtg v3 runtime initialization sequence, configuration model, and FastAPI app wiring.

Configuration (Settings)

qtg.config.Settings is a pydantic-settings based settings class. Every setting can be overridden by an environment variable with the MG_ prefix.

Environment variable catalog

Settings load order

Per SettingsConfigDict in pydantic-settings:
  1. .env file (project root)
  2. Environment variables (MG_ prefix)
  3. Environment variables win over .env
Unknown environment variables are ignored because extra="ignore" is set.

EVM RPC endpoint configuration

MG_EVM_RPC_ENDPOINTS_JSON is a JSON object string whose keys are chain IDs and whose values are RPC URLs.
parse_evm_rpc_endpoints_json() parses it and:
  • raises ValueError if the value is not a valid JSON object
  • skips keys whose value is None or an empty string
  • converts every key and value to str
An empty object ({}) skips CCTP executor and EVM probe registration.

CEX binding configuration

MG_CEX_BINDINGS_JSON is a JSON object string that resolves a logical venue alias into a concrete provider and credential bundle. Example:
Meaning:
  • key = internal QTG exchange key (binance_lab)
  • provider = concrete adapter factory name (binance)
  • master credentials and options = values injected when building adapters and fetchers
  • a Binance sub binding stores only sub_account_uid, not credentials
  • because of current Binance API constraints, that value must be the sub-account email, not a numeric subUserId / UID
Lookup order:
  1. MG_CEX_BINDINGS_JSON binding-first
  2. legacy per-adapter credentials and options from MG_UPBIT_*, MG_BINANCE_*, MG_OKX_*, and MG_BYBIT_*

CCTP Iris configuration

Settings for checking Circle CCTP (Cross-Chain Transfer Protocol) attestations through the Iris API. validate_http_base_url() validates the URL format. It must start with http:// or https://.

Pro-side graceful loading

main.py separates Pro registration into three guarded phases:
  1. collect_pro_dashboard_routers(settings) during create_app()
  2. register_pro_factories(settings) at the start of lifespan(), before bootstrap_runtime(settings=settings)
  3. register_pro_routes(app, settings) after bootstrap_runtime() completes
The graceful loader lives in src/qtg/pro_loader.py. When a Pro distribution is present, the loader reaches the Pro package’s registration hook through this single sanctioned boundary. OSS-only deployments skip these hooks transparently when no Pro package is installed.

Inbound HMAC auth

main.create_app() always installs HMACAuthMiddleware, and actual enforcement is controlled by MG_AUTH_ENABLED.
  • if MG_AUTH_ENABLED=false, the middleware bypasses requests
  • if MG_AUTH_ENABLED=true, every /v3/** request except /healthz must include:
    • X-QTG-Key-Id
    • X-QTG-Timestamp
    • X-QTG-Nonce
    • X-QTG-Signature
Canonical string:
  • PATH is the routed, percent-decoded path the server dispatches on — no origin, no query string.
  • QUERY is the query exactly as sent: no leading ?, no sorting, empty string if absent. Sorting before signing transmits bytes you did not sign, and every such request 401s.
On success, request.state.auth = {key_id, client_id, purpose} is injected. The key and nonce store is DB-backed. On app startup, app.state.auth_session_factory = AsyncSessionLocal is injected.

Auth seed CLI

Before enabling MG_AUTH_ENABLED=true in an operational environment, seed at least one caller key first.
Behavior:
  • creates ApiClient, or reuses the active client with the same name
  • creates a new ApiClientKey
  • generates a random secret when --hmac-secret is omitted
  • prints the result once as JSON on stdout
The first version is create-only. Revoke, rotate, and batch seeding are follow-up work.

bootstrap_runtime() initialization sequence

bootstrap_runtime(settings=settings) registers runtime dependencies. The actual sequence in src/qtg/infrastructure/bootstrap.py is:
Stargate is Pro. There is no stargate_enabled branch in the Free bootstrap.py. Stargate executors register from the Pro package, gated on a Pro-side setting, and are reached only through the pro_loader.register_pro_executors() boundary call shown above.

Representative executors and probes

This document focuses on the core executor families referenced elsewhere in docs/reference/: All local executors are registered through LocalExecutorAdapter.

FastAPI app startup sequence

create_app()

The whoami, audit, agent_wallet_topups, registry_verify_drift, hyperliquid, ccip_registry, admin_cutover, and admin_signers routers are mounted alongside the core movement/registry/balance routers. Pro-side routers are mounted separately after bootstrap_runtime() via register_pro_routes(app, settings).

lifespan context manager

FastAPI lifespan handles server startup and shutdown. On startup: On shutdown:

Important runtime characteristics

  1. bootstrap_runtime() runs once from lifespan: runtime registries are populated at the startup boundary, not when the app object is created.
  2. Schema is validated, not auto-created: Base.metadata.create_all() is no longer used. Instead boot_validate_schema_invariants(settings.network_mode) runs at startup and (a) rejects any non-PostgreSQL dialect (PG-only since v0.1.0), (b) asserts public and the process-selected network schema exist, and (c) checks the global and selected-network Alembic heads. The unselected network schema and version table are not required or queried. Fresh databases are bootstrapped through the global chain plus one selected network chain, not by the app:
  3. Runtime is network-class split and process-scoped: sessions are obtained via session_for(network_class), while iterate_per_network(...) defaults to the single process-selected network. Registry sync, worker-heartbeat seeding, and worker loops therefore use settings.network_mode unless a test or explicit administrative caller supplies another target. The in-memory engine/sessionmaker registry retains entries for both NetworkClass values; that capability does not require both physical schemas.
  4. Audit-descriptor boot check: after Pro routes are mounted, register_free_audit_descriptors() runs and then verify_all_mutating_routes_have_descriptors(app, ...) fails fast at boot if any mutating route lacks an audit descriptor (or an @audit_exempt marker).
  5. Gateway and balance fetcher registration are independent of EVM executor registration: Gateway and CEX balance fetchers can register before any EVM endpoints are available.
  6. Worker activation is conditional: background workers run only when MG_WORKERS_ENABLED=true. Beyond the 8 always-on workers, the conditional workers (evm_nonce_reaper, ccip_stranded_reaper, capital_transfer_followup, balance_snapshot, verify_drift, ccip_drift) are added only when their respective flags are set. See Runtime Workers.
  7. Logging initialization: logging.basicConfig(level=logging.INFO) is configured when main.py is imported.
  8. Toolchain baseline: both local development and the backend Docker image use the uv lockfile install path. The current minimum supported Python is 3.14.

Minimal configuration examples

API-only mode (development / test)

Full operational mode

CCTP-enabled mode