Files
notify-bridge/RELEASE_NOTES.md
T
alexei.dolgolyov 90def11b8d
Release / release (push) Successful in 1m3s
chore: release v0.4.0
2026-04-23 21:12:17 +03:00

8.4 KiB

v0.4.0 (2026-04-23)

A production-readiness release focused on hardening the service for real-world deployment: end-to-end structured logging with runtime controls, a broad security and runtime review across the HTTP, auth, DB, and scheduler layers, and a new pre-migration database snapshot that makes upgrades recoverable with a single file restore. Release CI and the Docker image build were also reworked for speed and reliability.

Features

  • Production-grade logging with per-request correlation (request_id / command / chat_id / bot_id / dispatch_id), secret masking in both messages and tracebacks, JSON or text format, runtime log level + per-module overrides editable from the settings UI, and env-var boot overrides (NOTIFY_BRIDGE_LOG_LEVEL / _FORMAT / _LEVELS). Closes every silent drop in the Telegram send path — /random and media-group failures now log WARN / ERROR with full context instead of disappearing (f50d465)
  • Production-readiness hardening across security, async, DB, and ops (920920b):
    • Security: async SSRF-safe DNS resolver; allow_redirects=False on all outbound clients; Matrix homeserver_url validation; rejection of ***-masked secrets on provider / email-bot updates; bcrypt moved off the event loop; JWT iss / aud + leeway with strict claim rejection; setup TOCTOU closed inside a transaction; expanded rate limits; constant-time login; config rejects known dev secret keys and validates CORS / ports / token lifetimes; webhook bodies capped at 1 MiB; Discord 429 retries bounded; CSP + HSTS headers added.
    • Async / runtime: SQLite engine tuned (WAL, synchronous=NORMAL, foreign_keys=ON, busy timeout, pool pre-ping); ordered lifespan shutdown; shared aiohttp session race-free; blocking storage / backup writes offloaded to threads; NUT client timeouts; Telegram poller switched from 3 s short-poll to 30 s + 25 s long-poll (~10x fewer API calls).
    • Database: new performance-index migration covering every FK and hot-path composite; new schema_version table; __system__ placeholder user (id=0) seeded to satisfy FKs; list_notification_trackers rewritten from 1+N+N*M to batched loads; retention job extended to event / webhook / action-execution logs.
    • Scheduler: AsyncIOScheduler job defaults set (coalesce, misfire_grace_time=300, max_instances=1).
    • Ops: uvicorn runs with proxy_headers / forwarded_allow_ips / graceful shutdown timeout; access log suppressed outside debug; FastAPI version read from importlib.metadata; new /api/ready endpoint; docker-compose adds resource / PID limits, read_only + tmpfs, cap_drop: ALL, no-new-privileges, drops the ALLOW_PRIVATE_URLS=1 default, and points healthcheck at /api/ready.
    • Frontend: /login redirects already-authenticated users to / and shows a distinct "backend unreachable" banner (en / ru) when /auth/needs-setup fails.
  • Pre-migration SQLite snapshots via VACUUM INTO at lifespan startup — takes a consistent, atomic copy of the DB before migrations run, so a botched upgrade is recoverable by restoring a single file. Safe under WAL; best-effort (failures log but never raise); configurable via NOTIFY_BRIDGE_PRE_MIGRATE_SNAPSHOT_KEEP (default 5; 0 disables). Snapshots land in data_dir/backups/pre-migrate-<ts>.db and the N oldest are pruned each boot (7cbb02b)

Bug Fixes

  • Allow unsafe-inline scripts in CSP so SvelteKit's hydration bootstrap inline <script> runs in production — without it the frontend failed to hydrate under the hardened CSP introduced in this release (8f0346e)

Upgrade Notes

  • ALLOW_PRIVATE_URLS=1 is no longer set by default in docker-compose.yml. If your deployment targets private network URLs, set it explicitly.
  • Docker healthchecks now probe /api/ready (separate from /api/health); update any external monitors accordingly.
  • Config startup now rejects known dev secret keys — set real values (e.g. JWT_SECRET) before upgrading.
  • Log format and level can now be changed at runtime from the settings UI; the log_format field still requires a restart to apply (a WARN is logged noting this).

Development / Internal

Tests

  • New packages/server/tests/ suite with 29 passing tests: config validation; JWT round-trip and aud / alg=none rejection; SSRF scheme and private-range enforcement (sync + async); Discord bounded retry; a lifespan-level /api/health + /api/ready smoke check. services/test_dispatch.py renamed to manual_dispatch.py so pytest no longer auto-collects production code (920920b)

CI / Build

  • CI now runs on push / PR with frontend svelte-check + build, and a non-push image build. Release workflow is gated on tests and publishes an immutable sha-<commit> image tag (920920b)
  • Install editable packages inside a venv (2bec253)
  • Cache pip downloads and collapse install into a single pip call (3b683ce)
  • Drop backend pytest from Gitea CI — editable install is too slow on the hosted runner (f904037)
  • Skip build.yml on release commits to avoid redundant runs (bbcdf1c)
  • Drop Trivy scan from release (output was discarded and it never failed) (19036a9)

Performance

  • Split external Docker deps into a cacheable layer and swap pip for uv for faster image builds (592e1b6)
  • Install uv from PyPI instead of the ghcr.io distroless image to avoid slow GHCR pulls on CI (a6a854a)

All Commits
Hash Message Author
8f0346e fix(csp): allow unsafe-inline scripts for SvelteKit hydration bootstrap alexei.dolgolyov
a6a854a perf(docker): install uv from PyPI instead of ghcr.io (avoid slow GHCR pulls) alexei.dolgolyov
19036a9 ci: drop trivy scan from release (never failed, output discarded) alexei.dolgolyov
592e1b6 perf(docker): split external deps into a cacheable layer, swap pip for uv alexei.dolgolyov
bbcdf1c ci: skip build.yml on release commits alexei.dolgolyov
f904037 ci: drop backend pytest stage (too slow on hosted runner) alexei.dolgolyov
3b683ce ci: cache pip downloads and collapse install into one pip call alexei.dolgolyov
2bec253 ci: install editable packages inside a venv alexei.dolgolyov
7cbb02b feat(db): pre-migration SQLite snapshots via VACUUM INTO alexei.dolgolyov
920920b feat: production-readiness hardening across security, async, DB, ops alexei.dolgolyov
f50d465 feat(logging): production-grade logging with context vars, secret masking, and runtime level control alexei.dolgolyov