PT-03: Make UMBRA_URL configurable via env var (default http://localhost).
Replaces hardcoded http://10.0.69.35 in notification dispatch job and
ntfy test endpoint. Add UMBRA_URL to .env.example.
PT-05: Add explicit path="/" to session cookie for clarity.
PT-06: Add concurrent session limit (MAX_SESSIONS_PER_USER, default 10).
When exceeded, oldest sessions are revoked. New login always succeeds.
PT-07: Escape LIKE metacharacters (%, _) in admin audit log action
filter to prevent wildcard abuse.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The W9 fix added user_id to SettingsResponse but missed the manual
_to_settings_response() builder, causing Pydantic validation failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ntfy columns to Settings model (server_url, topic, auth_token, enabled, per-type toggles, lead times)
- Create NtfySent dedup model to prevent duplicate notifications
- Create ntfy service with SSRF validation and async httpx send
- Create ntfy_templates service with per-type payload builders
- Create APScheduler background dispatch job (60s interval, events/reminders/todos/projects)
- Register scheduler in main.py lifespan with max_instances=1
- Update SettingsUpdate with ntfy validators (URL scheme, topic regex, lead time ranges)
- Update SettingsResponse with ntfy fields; ntfy_has_token computed, token never exposed
- Add POST /api/settings/ntfy/test endpoint
- Update GET/PUT settings to use explicit _to_settings_response() helper
- Add Alembic migration 022 for ntfy settings columns + ntfy_sent table
- Add httpx==0.27.2 and apscheduler==3.10.4 to requirements.txt
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>