Three bugs causing 100% CPU and container crash-looping in production:
1. sse-starlette ping=0 causes await anyio.sleep(0) busy loop in _ping task.
Each SSE connection spins a ping task at 100% CPU. Changed to ping=15
(built-in keepalive). Removed our manual ping yield in favor of continue.
2. Dockerfile purged curl after installing deno, but Docker healthcheck
(and compose override) uses curl. Healthcheck always failed -> autoheal
restarted the container every ~2 minutes. Keep curl in the image.
3. Downloads that fail during server shutdown leave zombie jobs stuck in
queued/downloading status (event loop closes before error handler can
update DB). Added startup recovery that marks these as failed.
Verified: no external URLs in frontend (no CDN fonts, no analytics,
no Google Fonts, no external scripts). All fonts use system fallback
chains (JetBrains Mono → Cascadia Code → Fira Code → monospace).
No outbound HTTP calls in backend code.
Added SecurityHeadersMiddleware enforcing:
- Content-Security-Policy: default-src 'self', script/font/connect
restricted to 'self', style allows 'unsafe-inline' for Vue scoped
styles, img allows data: URIs, object-src 'none', frame-ancestors
'none'
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- Referrer-Policy: no-referrer
These headers prevent any accidental introduction of external
resources in future development — CSP violations will block them.