Files
Imago/CHANGELOG.md
T

4.4 KiB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog.

[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.