Skip to content

Configuration

Create application.yaml in your project root. Top-level keys are core settings; each extension reads its own section (the section name is the provider’s config_key):

application.yaml
app_name: my-app
debug: false
env: development # development | staging | production | test
logging:
level: INFO
json_format: true # true | false
# quadkit-web (name: "web")
web:
server:
host: "0.0.0.0"
port: 8000
cors:
enabled: true
allow_origins: ["https://myapp.com"]
# quadkit-cli (config_section: "cli")
cli:
enabled: true
color: true
from quadkit import QuadKitConfig
# Auto-discovers application.yaml in the project root
config = QuadKitConfig.from_yaml()
# Or from a specific path
config = QuadKitConfig.from_yaml("path/to/application.yaml")

Application loads configuration for you when you don’t pass one — it calls QuadKitConfig.from_env_profile() by default.

QuadKitConfig has typed top-level fields:

FieldTypeDefaultDescription
app_namestr"quadkit-app"Application name
debugboolFalseDebug mode
envEnvironmentdevelopmentDeployment environment
loggingLoggingConfig—Structured logging settings
moduleslist[str][]Enabled modules

Extension sections (web:, cli:, …) are accessed via config.get_section().


There are two complementary mechanisms.

Use ${VAR} for secrets and deployment values, with optional defaults via ${VAR:default}:

application.yaml
web:
server:
host: "${HOST:0.0.0.0}"
port: "${PORT:8000}"

Any configuration key can be overridden by an environment variable using the QK_ prefix and double underscores for nesting. Env vars win over YAML:

Terminal window
QK_WEB__SERVER__PORT=9000 # web.server.port = 9000
QK_WEB__SECURITY__CORS__ALLOW_ORIGINS__0=https://myapp.com # list items use numeric indices
VariablePurposeDefault
QK_PROFILEActive configuration profile(none)
QK_DEBUGEnable debug modefalse
QK_QUIETSuppress startup bannerfalse
QK_ENVDeployment environmentdevelopment

QuadKit merges a profile-specific YAML over the base config. Set QK_PROFILE to activate it:

application.yaml # Base config (always loaded)
application.development.yaml # Merged when QK_PROFILE=development
application.staging.yaml # Merged when QK_PROFILE=staging
application.production.yaml # Merged when QK_PROFILE=production
application.test.yaml # Merged when QK_PROFILE=test
application.development.yaml
debug: true
logging:
level: DEBUG
json_format: false
web:
server:
port: 9000
application.production.yaml
debug: false
logging:
level: WARNING
json_format: true
cache:
backends:
- name: redis
type: redis
default: true
redis_url: "${REDIS_URL}"
from quadkit import QuadKitConfig
# Reads QK_PROFILE from the environment
config = QuadKitConfig.from_env_profile()
# Explicit profile
config = QuadKitConfig.from_env_profile("production")
# With a custom base path
config = QuadKitConfig.from_env_profile("staging", base_path="./config")

validate_for_environment() checks environment-specific constraints (for example, debug=True in production):

from quadkit.contracts.core.config import Environment
issues = config.validate_for_environment(Environment.PRODUCTION)

A provider declares config_key and config_model to automatically receive its typed config section — no manual parsing:

from dataclasses import dataclass
from quadkit import Provider
from quadkit.contracts.core.di import ContainerRegistrarProtocol
@dataclass
class BillingConfig:
stripe_key: str = ""
currency: str = "usd"
class BillingProvider(Provider):
name = "billing"
config_key = "billing" # reads "billing:" from application.yaml
config_model = BillingConfig # coerces it into BillingConfig
async def register(self, container: ContainerRegistrarProtocol) -> None:
cfg = self.config or BillingConfig() # self.config is a typed BillingConfig
container.singleton(StripeClient, StripeClient(cfg.stripe_key))

Before calling register(), the framework reads the matching section via QuadKitConfig.get_section(config_key, config_model) and assigns it to provider.config. Built-in providers use the same mechanism:

Providerconfig_key
DatabaseProvider"sql"
CacheProvider"cache"
AuthProvider"auth"

config = QuadKitConfig.from_yaml()
# Typed top-level access
config.app_name # "my-app"
config.debug # False
config.environment # Environment.DEVELOPMENT
# Section access (extension config)
db_config = config.get_section("sql", DatabaseConfig)
rag_config = config.get_section("ai_rag", RAGConfig) # dotted paths also supported
# Existence + serialization (secrets redacted by default)
config.has_section("web") # True
config.to_dict() # {"app_name": "...", "auth": {"secret_key": "***"}}
config.to_dict(redact_secrets=False) # full values