Configuration
Z8ter exposes a callable configuration service backed by Starlette Config. Builder methods queue setup; services become available after builder.build().
Upgrading to 0.3.1
Z8ter 0.3.1 requires starlette>=1.6,<2.0; 0.3.0 used a pre-1.0 Starlette range. Upgrade Z8ter and resolve its dependencies together rather than retaining an old Starlette pin. Form-token CSRF requests now preserve the body for handlers and enforce an 8 MiB default buffer limit; configure max_form_body_size or use a header token for larger streaming uploads.
Load and read configuration
from z8ter.builders.app_builder import AppBuilder
builder = AppBuilder()
builder.use_config(".env")
builder.use_templating()
builder.use_vite()
app = builder.build(debug=False)
asgi_app = app.starlette_app
config = app.state.services["config"]
app_name = config("APP_NAME", default="My app")
port = config("PORT", cast=int, default=8000)
Inside a handler, use request.app.state.services["config"]. For ordinary keys, process environment variables override the selected .env file, followed by a supplied default. A missing key without a default raises KeyError; a missing .env file is allowed.
The special BASE_DIR key uses a process environment override or Z8ter's resolved app directory. Set the actual project root with z8ter.set_app_dir(...) or Z8TER_APP_DIR; changing the config value alone does not relocate project files.
Types and validation
from starlette.datastructures import CommaSeparatedStrings
feature_enabled = config("FEATURE_ENABLED", cast=bool, default=False)
allowed_hosts = config("ALLOWED_HOSTS", cast=CommaSeparatedStrings, default="localhost")
secret_key = config("APP_SESSION_KEY")
if len(secret_key) < 32:
raise ValueError("APP_SESSION_KEY must contain at least 32 characters")
Boolean casting accepts the strings true, 1, false, and 0, ignoring case. Choose defaults and validate required application settings explicitly. Defining a setting such as ALLOWED_HOSTS does not install middleware that uses it.
Framework settings
APP_SESSION_KEY: signing key used by application sessions and, unless explicitly overridden, CSRF protection. App-session setup requires at least 32 characters.LOGIN_PATH: destination used bylogin_required; define it when using the guard.APP_PATH: destination used byskip_if_authenticated; define it when using the guard.EMAIL_PROVIDER:consoleby default, orsmtp, whenuse_email()is enabled.EMAIL_FROM: default sender for the email service.SMTP_HOST,SMTP_PORT,SMTP_USERNAME,SMTP_PASSWORD,SMTP_USE_TLS,SMTP_USE_SSL: settings read by the SMTP builder. Defaults are port 587, STARTTLS enabled, implicit SSL disabled.
APP_SESSION_KEY=replace-with-a-generated-private-key
LOGIN_PATH=/login
APP_PATH=/app/dashboard
EMAIL_PROVIDER=console
EMAIL_FROM=noreply@example.com
Session lifetime and cookie security are arguments to SessionManager, not automatic SESSION_TTL or SESSION_COOKIE_SECURE environment settings. See Authentication.
Settings read directly from the process environment
These are separate from the callable config service:
Z8TER_APP_DIR: fallback project root whenset_app_dir()has not been called.Z8TER_DEBUG:AppBuilder.build()usestruewhen no explicitdebugargument is supplied. An explicit argument wins. The 0.3.1 CLI sets a default before importingmain.py: false in prod, true in dev/LAN/WAN, while preserving an explicit environment value. Direct Uvicorn startup follows the debug choice inmain.py.Z8TER_MODE: the core app'sdev,prod, ortestmode; this is separate from Uvicorn's reload flag.Z8TER_APP_FACTORY: optional override for the CLI ASGI factory import path. In 0.3.1 the default loader reusesmain.asgi_app, thenmain.app, and falls back tomain.app_builder.build()only if neither built callable is present.DATABASE_URL: read byDatabase()and database CLI commands when no explicit URL is supplied.VITE_DEV_SERVER: optional Vite asset server URL, read whenz8ter.viteis imported.VITE_ALWAYS_RELOAD_MANIFEST:truedisables manifest caching, also read at import.
use_config(".env") reads values through the service; it does not export them into os.environ. Export process settings before launching Python, or explicitly pass config values to the relevant constructor. PORT and HOST are not interpreted by z8 run; use Uvicorn arguments or run_server(port=...) instead.
SQLite and other databases
The built-in Database, SQLiteUserRepo, and SQLiteSessionRepo support SQLite. PostgreSQL, Redis, and other stores require your own integrations/repositories; declaring a URL or pool setting does not install a driver.
Use an explicit absolute path to avoid ambiguous SQLite URL handling:
from pathlib import Path
from z8ter.database import Database, init_database
path = Path("data/app.db").resolve()
database_url = f"sqlite:///{path.as_posix()}"
db = init_database(url=database_url)
The current parser treats sqlite:///data/app.db as /data/app.db. An absolute path assembled as above yields four slashes on Unix. sqlite:///:memory: is supported, but connections are thread-local: use a temporary file database for tests that cross threads.
Frontend development and production
The generated starter uses Vite build-watch and a separate Tailwind CSS watcher. Its npm run dev starts those watchers and the Python server; it does not start a Vite HMR server. Leave VITE_DEV_SERVER unset for this workflow.
If you deliberately run a Vite dev server, export its URL before starting Python. Production uses static/js/.vite/manifest.json; build the assets with npm run build and leave VITE_DEV_SERVER unset. A missing manifest or entry produces an error; a configured dev server is not automatically replaced by the production manifest if unavailable.
Environments and secrets
Choose the config filename explicitly, for example builder.use_config(".env.production"). Z8ter does not merge multiple environment files automatically.
Keep secrets out of version control:
.env
.env.*
!.env.example
Generate a private key with:
python -c 'import secrets; print(secrets.token_hex(32))'
Use distinct keys per environment. Enable security middleware and production cookie settings explicitly; a configuration file alone does not enable them.