FastMCP auto-enables DNS rebinding protection when host=127.0.0.1
(the default). Production requests from brain.woltje.com were rejected
with 421 Invalid Host header because the allowed_hosts list was empty.
Added MCP_ALLOWED_HOSTS config field (comma-separated). When set,
DNS rebinding protection is enabled with those hosts; when empty,
protection is disabled. Set MCP_ALLOWED_HOSTS=brain.woltje.com in
Portainer stack env.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
StreamableHTTPSessionManager requires its run() context manager to be
active before handling requests. Without it, every MCP call returned
RuntimeError: Task group is not initialized.
Also changed mount from /mcp to / (at end of route list) so the MCP
endpoint is accessible at /mcp rather than /mcp/mcp, matching the
sub-app's internal route structure.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
brain-api now takes DATABASE_URL as a complete connection string from env
instead of constructing it from POSTGRES_PASSWORD. This matches Portainer's
env var pattern and avoids password duplication.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
TLS terminates at Cloudflare/pfSense, not Traefik.
Confirmed by inspecting working services (nextcloud, sage-phr):
- entrypoints=web (not websecure)
- no tls or certresolver labels needed
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Switch from plugins/kaniko to gcr.io/kaniko-project/executor:debug
- Use gitea_username/gitea_token secret names (matches org pattern)
- Use YAML anchor for docker config setup
- Fix image destination to use shell variable (YAML anchors don't expand in commands)
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>