Configuration

How AppConfig works — set fields in code for dev, load from the environment for prod, both produce the same frozen config.

Page actions AI-ready formats and sharing
Open LLM text
Share with AI
Ask Claude Ask ChatGPT Ask Gemini Ask Copilot

Overview

Chirp reads all configuration from one immutable object:AppConfig. You set fields in code for local development, or load them from environment variables for production — both produce the same frozen config that the app runs against.

For most apps, start here:

# Set fields in code
from chirp import App, AppConfig

app = App(config=AppConfig(debug=True, secret_key="dev-only"))
# Load from the environment
from chirp import App, AppConfig

app = App(config=AppConfig.from_env())  # reads CHIRP_SECRET_KEY, CHIRP_ENV, CHIRP_PORT, ...

Pass no config and you get the defaults:

app = App()  # same as App(config=AppConfig())

Every field has IDE autocomplete and type checking, so there are no runtime KeyErrorsurprises. The fields you reach for most often are below; the full catalog and exact signatures live in the complete field and signature reference.

Load configuration from the environment

AppConfig.from_env()builds a config from environment variables — the 12-factor path you want in production. Unset variables fall back to theAppConfig defaults, and any keyword you pass overrides the result:

app = App(config=AppConfig.from_env(template_dir="pages", worker_mode="async"))

Variables use the CHIRP_ prefix (override with from_env(prefix="MYAPP_")). The commonly-set ones:

Variable Sets
CHIRP_SECRET_KEY secret_key
CHIRP_ENV env (development / staging / production)
CHIRP_DEBUG debug
CHIRP_HOST, CHIRP_PORT host, port
CHIRP_ALLOWED_HOSTS allowed_hosts(comma/space-separated)
CHIRP_TRUSTED_PROXIES trusted_proxies(comma/space-separated)
CHIRP_FEATURE_<NAME> afeature_flags entry (CHIRP_FEATURE_BETA=true)

Ifpython-dotenv is installed (pip install chirp[config]), from_env()loads a.envfile from the current directory first. On Railway, it falls back to the platform'sPORT, binds 0.0.0.0, and — when CHIRP_ALLOWED_HOSTSis unset — setsallowed_hosts to your RAILWAY_PUBLIC_DOMAINplus healthcheck.railway.app.

Fields you set by hand

These are the knobs most apps touch directly. Everything else has a default that works out of the box.

Field Type Default What it does
debug bool False Rich error pages, DevTools, template auto-reload
secret_key str "" Signing key for sessions, CSRF, and signed state
host str "127.0.0.1" Bind address forapp.run() / chirp run
port int 8000 Bind port forapp.run() / chirp run
env str "development" Environment label; a non-development value requires asecret_key
template_dir str | Path "templates" Directory for Kida templates
static_dir str | Path | None "static" Static-file directory;Nonedisables default static serving
allowed_hosts tuple[str, ...] ("*",) Host allowlist for production host validation
worker_mode str "auto" Pounce worker execution:"auto", "sync", "async", or "subinterpreter"

The remaining fields group by concern below — open the one you need.

Debug mode

Whendebug=True:

  • Detailed error pages with tracebacks are shown in the browser.
  • Templates auto-reload when modified (no server restart).
  • Stricter validation warnings are surfaced.
app = App(config=AppConfig(debug=True, secret_key="dev-only"))

Secret key

secret_keysigns sessions, CSRF tokens, and signed state. Use a strong random value in production, and read it from the environment rather than hard-coding it:

import os

app = App(config=AppConfig(secret_key=os.environ["CHIRP_SECRET_KEY"]))

Reference field groups

Provisional fields

New in 0.8

Construction-time validation

AppConfig.__post_init__enforces a few invariants when you construct the config, so misconfigurations fail loud immediately instead of deep in the launch path:

  • Secret key — emptysecret_key with env not "development" raises ConfigurationError.
  • Upload caps — ifmax_upload_size exceeds max_request_body_size, Chirp clamps it to the body cap when the upload cap is still at its default, and otherwise raises ConfigurationError.
  • Forwarded hopsforwarded_for_trusted_hops below 1 raises ConfigurationError. To ignore X-Forwarded-For, leave trusted_proxiesempty rather than lowering this.

See also