Files
ai.allucanget.biz/docs/8-crosscutting-concepts.md
T
2026-04-27 18:19:40 +02:00

75 lines
3.5 KiB
Markdown

# 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": "<code>", "message": "<description>" }`.
- 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.