# 8. Cross-cutting Concepts Describes crosscutting concepts (practices, patterns, regulations or solution ideas). Such concepts are often related to multiple building blocks. They may include many different topics such as domain models, architecture patterns, rules for using specific technology, security, logging, and error handling. > Pick **only** the most-needed topics for your system. ## Security - All API endpoints (except `/auth/login`) require a valid JWT in the `Authorization: Bearer` header. - HTTPS enforced in production via reverse proxy (nginx or Caddy). - Passwords stored as bcrypt hashes. ## Logging - Structured JSON logs from FastAPI via Python `logging` + `structlog`. - OpenTelemetry traces exported for observability. - Log level configurable via environment variable `LOG_LEVEL`. ## Error Handling - All API errors return a unified JSON shape: `{ "error": "", "message": "" }`. - HTTP status codes follow REST conventions (400, 401, 403, 404, 422, 500). ## Configuration - All secrets (API keys, DB path, JWT secret) loaded from environment variables or `.env` file. - No secrets committed to source control. ## DuckDB Concurrency and Storage ### Single Writer Per Process DuckDB allows only one process to open the database file in read-write mode at a time. The FastAPI backend must be run with a single worker (`uvicorn --workers 1`). Running multiple workers against the same DuckDB file will cause startup errors. ### asyncio.Lock for Writes All database write operations (`INSERT`, `UPDATE`, `DELETE`) in the FastAPI async context are wrapped in a single `asyncio.Lock` (`get_write_lock()` from `backend/app/db.py`). This prevents concurrent coroutines from issuing overlapping writes within the single process, which would otherwise raise DuckDB optimistic concurrency errors. Read operations (`SELECT`) do not require the lock — DuckDB's MVCC provides consistent read snapshots. ### Schema ```sql CREATE TABLE users ( id UUID DEFAULT uuid() PRIMARY KEY, email VARCHAR NOT NULL UNIQUE, password_hash VARCHAR NOT NULL, role VARCHAR DEFAULT 'user', created_at TIMESTAMP DEFAULT now(), updated_at TIMESTAMP DEFAULT now() ); CREATE TABLE refresh_tokens ( jti UUID DEFAULT uuid() PRIMARY KEY, user_id UUID NOT NULL, -- soft FK to users.id issued_at TIMESTAMP DEFAULT now(), expires_at TIMESTAMP NOT NULL, revoked BOOLEAN DEFAULT false ); ``` > The `REFERENCES users(id)` foreign key is intentionally omitted from `refresh_tokens`. DuckDB fires FK checks on `UPDATE` of the parent table (including email changes), causing false constraint violations. Referential integrity is enforced manually: deleting a user also deletes their refresh tokens in the same write transaction. ### Access Tokens Access tokens are **stateless** JWTs — not stored in the database. They are validated by signature and expiry claim only. The short TTL (15 minutes) limits the blast radius if a token is leaked. ### Refresh Tokens Refresh tokens store a JTI (JWT ID) UUID in the `refresh_tokens` table. On each use the old JTI is revoked and a new one issued (rotation). On logout the JTI is immediately revoked. Expired and revoked tokens can be purged via `POST /admin/tokens/purge`. ### Future: AI Generation History AI generation metadata (model, prompt, cost, result URLs) can be stored as JSON columns in a future `generation_history` table in DuckDB, enabling per-user analytics and usage dashboards at zero extra infrastructure cost. ## OpenRouter API Integration ### Image Generation Image generation uses two different OpenRouter endpoints depending on the model: - **Legacy endpoint** (`/images/generations`): Used by DALL-E 3 and similar models. Returns `data[].url` and `data[].b64_json`. - **Chat completions** (`/chat/completions` with `modalities: ["image"]`): Used by FLUX.2 Klein 4B and GPT-5 Image Mini. Returns `choices[0].message.images[].image_url.url` as base64 data URLs. The router auto-detects the model type and routes accordingly. Image configuration (`aspect_ratio`, `image_size`) is passed via `image_config` for chat-based models. ### Video Generation Video generation uses OpenRouter's `/api/v1/videos` endpoint with a **submit-and-poll** pattern: 1. `POST /api/v1/videos` with `model`, `prompt`, `aspect_ratio`, `resolution`, `duration_seconds` 2. Response: `{"id": "job_id", "polling_url": "https://..."}` with `status: "queued"` 3. Poll `GET polling_url` every 5 seconds until `status` is `"completed"` or `"failed"` 4. Completed response includes `unsigned_urls: [str]` array with video download URLs Supported models: `openai/sora-2-pro`, `google/veo-3.1-fast`. Both text-to-video and image-to-video use the same `/api/v1/videos` endpoint (image-to-video includes `image_url` in the request body).