0012. Configuration by environment, safe defaults¶
Status: accepted
Date: 2026-11-07
Context¶
Housecairn is self-hosted by people who will not read Django’s deployment checklist. The settings they get without changing anything must be the safe ones (principle P7), and the same image must run anywhere.
Decision¶
One settings module,
housecairn.settings, configured only by environment variables through django-environ. A.envfile at the repository root, if present, fills in missing variables for development; it never overrides the environment and is never committed (.env.exampledocuments the variables).DEBUGis false by default.SECRET_KEYis required unlessDEBUGis true.ALLOWED_HOSTSis empty by default in production.Database:
DATABASE_URL, SQLite inHOUSECAIRN_DATA_DIRby default, PostgreSQL possible.With
DEBUGoff: HTTPS redirect (except/healthz), secure,HttpOnly,SameSite=Laxcookies, HSTS for one year,X-Frame-Options: DENY, no sniffing,same-originreferrer and opener policies.HOUSECAIRN_BEHIND_TLS_PROXY=truetrustsX-Forwarded-Protofrom a reverse proxy.Forwarding headers (
X-Forwarded-For,X-Forwarded-Proto,X-Forwarded-Host,Forwarded,X-Real-IP…) count only from the peers ofHOUSECAIRN_FORWARDED_ALLOW_IPS, by default the loopback. The first middleware,housecairn.core.proxy.ForwardedHeadersMiddleware, removes them from a request of any other peer, and for a trusted proxy replacesREMOTE_ADDRwith the right-most address ofX-Forwarded-Forthat is not a trusted proxy. django-allauth’s rate limits and everything else readREMOTE_ADDRonly (ALLAUTH_TRUSTED_PROXY_COUNT = 0). gunicorn gets the same list (ADR 0015).USE_X_FORWARDED_HOSTstays off: theHostheader names the site.HSTS
includeSubDomainsandpreloadcommit the whole domain, which only its owner can decide: they stay off by default, configurable, and Django’s reminders about them (security.W005,security.W021) are silenced./healthzanswers{"status": "ok"}or 503 without authentication and without any other information.Passwords: at least 12 characters, plus Django’s similarity, common and numeric validators.
deploy/production.env.exampleis an example production environment;manage.py check --deploy --fail-level WARNINGpasses with it and a generatedSECRET_KEY, and a test enforces this.
Consequences¶
Running without a
SECRET_KEYin production fails at start-up instead of running insecurely.Local development needs
DEBUG=true, as in.env.example.