A reliability and security hardening pass — config writes and the search-history DB are now safe under concurrent access and failure, the scheduler fails safely and escalates repeated failures, the web UI's attack surface is narrowed (CSP nonce, URL/secret validation), and the dashboard surfaces a per-app "Last OK" timestamp so a silently stuck connection is visible at a glance. A settings-save bug (General fields detached from the Save button) was caught by the deployed-build walkthrough and fixed.
16/16 requirements satisfied (SAFETY ×6, SEC ×4, RES ×3, TEST ×4) across phases 64–67. Milestone audit passed; deep-review APPROVED; live walkthrough passed.
Key accomplishments
- Data safety (Phase 64): SQLite search_history gained a two-bound contract — resolved rows trim inline to
max_history_rows, pending rows cap at2× maxvia aPendingCapExceededguard so a stalled tracker can't grow the table unboundedly. Atomic TOML config writes now log/re-raiseos.replacefailures, and a config-write lock (AST-audited in CI) serializes concurrent saves. - Scheduler resilience (Phase 65): Narrowed the cycle exception handler to a four-type tuple + an APScheduler
EVENT_JOB_ERRORlistener so code-bug exceptions become operator-visible instead of silently swallowed; per-(app,instance) consecutive-failure counter escalates WARNING→ERROR at a configurable threshold; graceful-shutdown drain extended 35s→60s and names the stuck cycle on timeout. - Security hardening (Phase 66): Removed
'unsafe-inline'from CSPscript-srcvia a per-request nonce (style-src retains it for Tailwind); settings reject an *arr URL carrying anapikey=query param; Basic-auth decoding rejects control-char credentials with a logged WARNING; startup warns on a too-short or unpersisted session secret. - Observability (Phase 67, RES-02): Each dashboard app card shows a "Last OK" timestamp — the last successful cycle — flagged amber when older than 2× the search interval, and shown even when the instance is unreachable.
- Performance (Phase 67, RES-03): Tag lists are cached per instance for 1 hour (monotonic TTL) instead of re-fetched every cycle, with targeted invalidation on config save / instance removal; manual Search Now uses the cache too.
- Test coverage (Phase 67, TEST-01): OriginCheckMiddleware CSRF suite covers missing Origin/Referer, both-absent, scheme-mismatch (pinned ALLOW, documented), and spoofed-host/suffix/port (REJECT).
- Walkthrough fix: General settings fields lived in a form separate from the "Save Settings" button, so saving silently reset them (including "Skip Unreleased Movies"). Fixed via
form="settings-form"association and verified live on the redeployed build.
Full Changelog: v2.7.3...v2.8.0