8.4 KiB
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 —/randomand media-group failures now logWARN/ERRORwith full context instead of disappearing (f50d465) - Production-readiness hardening across security, async, DB, and ops (920920b):
- Security: async SSRF-safe DNS resolver;
allow_redirects=Falseon all outbound clients; Matrixhomeserver_urlvalidation; rejection of***-masked secrets on provider / email-bot updates; bcrypt moved off the event loop; JWTiss/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; sharedaiohttpsession 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_versiontable;__system__placeholder user (id=0) seeded to satisfy FKs;list_notification_trackersrewritten from1+N+N*Mto batched loads; retention job extended to event / webhook / action-execution logs. - Scheduler:
AsyncIOSchedulerjob 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 fromimportlib.metadata; new/api/readyendpoint; docker-compose adds resource / PID limits,read_only+ tmpfs,cap_drop: ALL,no-new-privileges, drops theALLOW_PRIVATE_URLS=1default, and points healthcheck at/api/ready. - Frontend:
/loginredirects already-authenticated users to/and shows a distinct "backend unreachable" banner (en / ru) when/auth/needs-setupfails.
- Security: async SSRF-safe DNS resolver;
- Pre-migration SQLite snapshots via
VACUUM INTOat 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 viaNOTIFY_BRIDGE_PRE_MIGRATE_SNAPSHOT_KEEP(default 5; 0 disables). Snapshots land indata_dir/backups/pre-migrate-<ts>.dband the N oldest are pruned each boot (7cbb02b)
Bug Fixes
- Allow
unsafe-inlinescripts 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=1is no longer set by default indocker-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_formatfield still requires a restart to apply (aWARNis logged noting this).
Development / Internal
Tests
- New
packages/server/tests/suite with 29 passing tests: config validation; JWT round-trip andaud/alg=nonerejection; SSRF scheme and private-range enforcement (sync + async); Discord bounded retry; a lifespan-level/api/health+/api/readysmoke check.services/test_dispatch.pyrenamed tomanual_dispatch.pyso 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 immutablesha-<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.ymlon 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 |