Skip to main content

Configuration

Every setting is read once at startup via pydantic-settings with env_prefix="KX_", extra="forbid" and frozen=True. Invalid combination fails fast with a ValueError before the app binds.

Environment variables

VariableTypeDefaultValidation
KX_HOSTstr127.0.0.1bind target; 0.0.0.0 only if intentionally exposing on LAN
KX_PORTint 1–655358000
KX_WORKERSint >=11!=1 rejected — JSON store is single-process
KX_RUNTIME_ROOTPathruntimecreated 0o700; holds state.json, runtime.lock, uploads/, .kxinspect-runtime
KX_SEED_ROOTPathapp/seedseed JSON directory
KX_STATIC_ROOTPathapp/static/photosseed media dir
KX_ENABLE_DEV_ROUTESboolfalsewhen false /_dev/* not even bound
KX_DEV_TOKENstr""required non-empty when dev routes enabled; checked hmac.compare_digest
KX_CORS_ORIGINStuple[str,…] (comma-split)http://localhost:8080,http://127.0.0.1:8080* rejected
KX_GRACE_PERIOD_DAYSint 0–365030used when dev-raised charge omits it
KX_DEMO_NOWstr? (ISO-8601 UTC Z)Nonewhen set, AnchoredDemoClock replaces SystemClock
KX_SSE_HEARTBEAT_SECONDSfloat >015.0SSE comment heartbeat interval
KX_SSE_QUEUE_CAPACITYint >=164per-client broker queue

Parsing helpers: cors_origins comma-split and trimmed, wildcard reject; demo_now validated via parse_instant (rejects offsets/naive); model_validator enforces workers==1 + token non-empty when needed.

Derived paths

settings.state_path # runtime/state.json
settings.uploads_root # runtime/uploads
settings.staging_root # runtime/uploads/.staging
settings.lock_path # runtime/runtime.lock
settings.marker_path # runtime/.kxinspect-runtime (RUNTIME_MARKER_UUID guard)

Constants (app/core/config.py)

REFERENCE_NOW = "2026-08-01T12:00:00Z"
CONTEST_REASON_MIN = 10 # graphemes
CONTEST_REASON_MAX = 2000
MAX_ATTACHMENTS = 5
MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024
MAX_ATTACHMENT_TOTAL_BYTES = 25 * 1024 * 1024
EVENT_RING_CAPACITY = 100
SCHEMA_VERSION = CONTRACT_VERSION = SEED_VERSION = API_MAJOR = "1"

Runtime files

runtime/
├── state.json # StoreSnapshot (atomically replaced)
├── runtime.lock # advisory flock, pid written
├── .kxinspect-runtime # RUNTIME_MARKER_UUID
├── uploads/
│ ├── att_<32hex> # finalized uploads (private)
│ └── .staging/<reqId>/ # transient per-request staging
└── .marker

Directory runtime/ is 700; lock open mode 0o600.

CORS & headers

Allowed headers: Content-Type, Idempotency-Key, Last-Event-ID, X-Dev-Token. Allowed methods: GET, POST, OPTIONS. Wildcard origin * rejected at startup; explicitly list every web origin.

Dev routes guard

When KX_ENABLE_DEV_ROUTES=true the router includes app/api/v1/endpoints/dev.py; every handler performs if not hmac.compare_digest(token, settings.dev_token): raise 401. Loopback does not bypass.

Example invocations

# production-like demo anchor (deterministic deadlines)
KX_DEMO_NOW=2026-08-01T12:00:00Z KX_CORS_ORIGINS=http://localhost:3000 \
uv run uvicorn app.main:create_app --factory --host 127.0.0.1 --port 8000

# with dev reset enabled
KX_ENABLE_DEV_ROUTES=true KX_DEV_TOKEN=local-demo \
uv run uvicorn app.main:create_app --factory

# custom runtime location for parallel tests
KX_RUNTIME_ROOT=/tmp/kx-test-$$ \
uv run pytest -q

No config is read outside Settings; tests inject Settings(runtime_root=tmp) + ManualClock to keep CI deterministic.