The API image ran four uvicorn workers, and each one built a full flow
controller — four sets of MQTT subscriptions, cron ticks and webhooks.
Runs one worker now; scaling out is the worker split, not more processes.
Adds a loop-lag watchdog and a deep /utils/health/ that fails when the
event loop is wedged or Redis is unreachable, the two failure modes a
process-alive check never sees. Autoheal restarts on that signal, behind
a compose profile because it mounts the Docker socket.
The private user-seeding routes now need an explicit opt-in rather than
just ENVIRONMENT=local, so a deployment that kept the default never
exposes them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011LF61rxW1FG5YCD2J9YqjY
Move compose.yml/compose.override.yml/compose.traefik.yml into docker/ and
split into the explicit prod -> dev -> local layering; compose.override.yml
had to be renamed because docker compose auto-loads that filename, which
defeats the layering.
- external network traefik-public -> proxy (shared with the website stack)
- frontend host dashboard.${DOMAIN} -> app.${DOMAIN}
- stable container_names, security_opt no-new-privileges on prod services
- adminer bound to 127.0.0.1 in dev instead of all interfaces
- .env.example replaces the committed .env
- pre-commit biome hook ran npm in a bun repo
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>