# HTTP API ## 1. High-Level Overview **Purpose**: FastAPI application that exposes HTTP endpoints for health monitoring, user authentication, encrypted resource loading/uploading, and a background Docker image unlock workflow. **Architectural Pattern**: Thin controller — delegates all business logic to Resource Management (03) and binary_split. **Upstream dependencies**: Core Models (01) — UnlockState enum; Resource Management (03) — ApiClient, binary_split functions **Downstream consumers**: None — this is the system entry point, consumed by external HTTP clients. ## 2. Internal Interfaces ### Interface: Module-level Functions | Function | Input | Output | Description | |-------------------|---------------------------------|----------------|---------------------------------| | `get_api_client` | — | ApiClient | Lazy singleton accessor | | `_run_unlock` | `str email, str password` | — | Background task: full unlock flow | ## 3. External API Specification | Endpoint | Method | Auth | Rate Limit | Description | |--------------------|--------|----------|------------|------------------------------------------| | `/health` | GET | Public | — | Liveness probe | | `/status` | GET | Public | — | Auth status + model cache dir | | `/login` | POST | Public | — | Set user credentials | | `/load/{filename}` | POST | Implicit | — | Download + decrypt resource | | `/upload/{filename}`| POST | Implicit | — | Encrypt + upload resource (big/small) | | `/unlock` | POST | Public | — | Start background Docker unlock | | `/unlock/status` | GET | Public | — | Poll unlock workflow progress | "Implicit" auth = credentials must have been set via `/login` first; enforced by ApiClient's auto-login on token absence. ### Request/Response Schemas **POST /login** ```json // Request {"email": "user@example.com", "password": "secret"} // Response 200 {"status": "ok"} // Response 401 {"detail": "error message"} ``` **POST /load/{filename}** ```json // Request {"filename": "model.bin", "folder": "models"} // Response 200 — binary octet-stream // Response 500 {"detail": "error message"} ``` **POST /upload/{filename}** ``` // Request — multipart/form-data data: folder: "models" (form field, default "models") // Response 200 {"status": "ok"} ``` **POST /unlock** ```json // Request {"email": "user@example.com", "password": "secret"} // Response 200 {"state": "authenticating"} // Response 404 {"detail": "Encrypted archive not found"} ``` **GET /unlock/status** ```json // Response 200 {"state": "decrypting", "error": null} ``` ## 4. Data Access Patterns ### Caching Strategy | Data | Cache Type | TTL | Invalidation | |---------------|---------------------|---------------|---------------------| | ApiClient | In-memory singleton | Process life | Never | | unlock_state | Module global | Until next unlock | State machine transition | ## 5. Implementation Details **State Management**: Module-level globals (`api_client`, `unlock_state`, `unlock_error`) protected by `threading.Lock` for unlock state mutations. **Key Dependencies**: | Library | Version | Purpose | |----------------|---------|------------------------------| | fastapi | latest | HTTP framework | | uvicorn | latest | ASGI server | | pydantic | (via fastapi) | Request/response models | | python-multipart| latest | File upload support | **Error Handling Strategy**: - `/login` — catches all exceptions, returns 401 - `/load`, `/upload` — catches all exceptions, returns 500 - `/unlock` — checks preconditions (archive exists, not already in progress), then delegates to background task - Background task (`_run_unlock`) catches all exceptions, sets `unlock_state = error` with error message ## 6. Extensions and Helpers None. ## 7. Caveats & Edge Cases **Known limitations**: - No authentication middleware — endpoints rely on prior `/login` call having set credentials on the singleton - `get_api_client()` uses a global without locking — race on first concurrent access - `/load/{filename}` has a path parameter `filename` but also takes `req.filename` from the body — the path param is unused - `_run_unlock` silently ignores `OSError` when removing tar file (acceptable cleanup behavior) **Potential race conditions**: - `unlock_state` mutations are lock-protected, but `api_client` singleton creation is not - Concurrent `/unlock` calls: the lock check prevents duplicate starts, but there's a small TOCTOU window between the check and the `background_tasks.add_task` call **Performance bottlenecks**: - `/load` and `/upload` are synchronous — large files block the worker thread - `_run_unlock` runs as a background task (single thread) — only one unlock can run at a time ## 8. Dependency Graph **Must be implemented after**: Core Models (01), Resource Management (03) **Can be implemented in parallel with**: — **Blocks**: — (entry point) ## 9. Logging Strategy No direct logging in this component — all logging is handled by downstream components via `constants.log()` / `constants.logerror()`. **Log format**: N/A (delegates) **Log storage**: N/A (delegates)