# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [2.0.0] - 2026-06-22 ### Security - API Key hashing now uses SHA-256 + pepper (SECRET_KEY) with timing-safe comparison via `secrets.compare_digest()` - CORS validation rejects `["*"]` when `allow_credentials=True` (Pydantic model_validator) - Scope validation: `ClientCreate.scopes` rejects invalid values via `field_validator` - Master key comparison uses constant-time compare ### Added - **Dead Letter Queue (DLQ)**: Failed ARQ jobs pushed to Redis DLQ after `PIPELINE_MAX_RETRIES` attempts. Admin endpoints to list/retry/clear dead jobs (`GET/POST/DELETE /admin/api/queue/dead`) - **Circuit Breaker AI**: All AI calls wrapped with `asyncio.wait_for()` + exponential backoff retry (configurable via `AI_REQUEST_TIMEOUT`, `AI_MAX_RETRIES`) - **Request ID Middleware**: `X-Request-ID` header injected in all responses + bound to structlog context - **Rate Limiting per Plan**: Dynamic rate limits based on client plan (free/standard/premium) via ContextVar - **Redis Rate Limit Storage**: Optional persistence via `RATE_LIMIT_STORAGE_URL` - **Worker script**: `worker.py` for standalone ARQ worker launch - **Docker Compose stack**: PostgreSQL 16 + Redis 7 + MinIO + API + Worker - **`.env.example`**: All configuration variables documented - **Grafana Dashboard**: `docs/grafana-dashboard.json` with 10 panels (images, tokens, pipeline, storage, WebSocket, errors) - **Database indexes**: Composite indexes `(client_id, uploaded_at)` and `(client_id, processing_status)` on images table ### Changed - `APP_VERSION` aligned to `2.0.0` in config (was `1.0.0`) - `delete_files()` now fully async — uses `await backend.delete()` instead of fire-and-forget `ensure_future()` - Upload flow: files deleted on DB commit failure (no orphaned S3 files) - Gemini client: cached with TTL — recreated if `GEMINI_API_KEY` changes - `parse_redis_url()` extracted to `app/workers/redis_client.py` (DRY — was duplicated in main.py and image_worker.py) - Rate limit key function encodes plan for per-plan bucket isolation - `_FallbackArqPool` moved to `app/workers/arq_fallback.py` (avoids circular imports) - ARQ worker: configurable `PIPELINE_TIMEOUT` and `PIPELINE_MAX_RETRIES`, `on_shutdown` handler - Pipeline: `push_dead_job()` on final retry failure marks image as `ERROR` with DLQ metadata - Admin panel: VITE_API_BASE_URL changed from internal Docker hostname to `http://localhost:8000` ### Fixed - ARQ fallback now returns explicit warning in `UploadResponse.message` instead of silently ignoring - Removed obsolete comment in `ai_vision.py` about `StorageBackend.read` - Removed signal handler instability in test fixtures - Fixed Docker Compose admin connectivity (browser couldn't resolve internal `backend` hostname) ## [1.0.0] - Previous ### Added (Phase 3: DX) - **WebSockets / Real-Time**: - Subscriptions on per-image workflows (`/ws/pipeline/{image_id}`). - Redis 60-second Event Buffering for reconnections. - Admin Monitor WebSocket feed (`/ws/admin/monitor`). - **REST API Versioning**: - Global `/api/v1/` prefix for structural stability. - Added strict API Sunset Date tracking middleware. - **Python SDK (`imago-client`)**: - Publishable PyPI-ready package located inside `sdk/`. - Automatic `httpx` HTTP reconnects, WebSockets streaming natively wrapped, and robust generic Error classes (`ImagoError`, `AuthError`, etc.). - **Admin Backend APIs**: - Complete JSON statistics (`/admin/api/stats`), global client configurations (`/admin/api/clients/*`) and Queue observability natively piped to the existing React Interface. - **Shaarli Integrations**: - Skeleton integration middleware (`integration/shaarli`) capable of bridging Shaarli's events right into Imago using the official SDK. - **Extensive Documentation**: - Modular guides provided inside `docs/` detailing WebSockets, Integrations, and SDK consumption. ### Changed - All API routing endpoints have been moved behind the `/api/v1` namespace. Existing API Clients must update base URLs from `/` to `/api/v1/`. ### Fixed - Fixed unawaited coroutines in async mocks causing `TypeError` on Pytest environments. - Fixed an incorrect payload key mapping in `APIClient` responses causing 500s (`AttributeError`). - Mocked S3 and ARQ correctly globally to ensure local automated tests pass completely isolated from network logic.