# Inspector backend architecture — START HERE The Inspector is a single-worker Flask app serving a Svelte 5 SPA. It manages the lifecycle of Quran-recitation forced-alignment deliveries (catalogue → align → review → release → publish) and lets editors trim/split/merge/re-reference word-level segments. The deployed app is a Docker HF Space; its source of truth is a container-local SQLite DB (`inspector.db`) full-file synced to an HF bucket after every committed write. Per-reciter content (`detailed.json`, `segments.json`, `timestamps/.json`, peaks, audio) stays JSON/binary on the bucket, read on demand. ## Layering ``` HTTP ─▶ routes//* thin Flask blueprints: parse → service → jsonify │ (primary Flask layer; only services/ exception is auth/auth.py) ▼ services//* business logic, Flask-free, returns plain dicts │ db/ storage/ audio/ auth/ state/ segments/ │ validation/ activity/ reference/ quran_foundation/ ▼ domain/ adapters/ utils/ pure model · JSON↔domain conversion · helpers services/storage/cache.py single owner of every mutable cache var config.py · constants.py env-overridable tunables · domain literals ``` Invariants: - **Routes are the Flask layer.** Services accept params, return dicts, raise typed errors; `app.py` maps those errors to the `{error: str}` JSON envelope. The sole `services/` exception is `services/auth/auth.py` — authlib's `flask_client` OAuth integration (`_CacheStateOAuth`/`_CacheStateIntegration` subclass Flask integration) cannot be abstracted away. The `services-flask-free` CI guard enforces this with `auth.py` as the only allow-listed file. - **Single worker.** `app.py::_assert_single_worker` refuses to boot under any multi-worker signal. State store, per-slug locks, role cache, OAuth-state cookie, and all in-memory caches assume one process. - **SQLite is the source of truth** for the 7 former bucket JSON stores + audit. Reads go through `services/db/repo_*`; writes commit in `services/db/connection.py::transaction()` then sync via `services/db/sync.py`. - **Bucket-first reads** for reciter content go through `services/storage/data_dir.py` + `storage_paths.py`, never raw `Path.read_text()`. - **`services/__init__.py` re-exports** every submodule at the legacy flat name (`from services.cache import …` still resolves to `services/storage/cache.py`) via `sys.modules` aliasing. New modules drop into a subpackage + one re-export line. ## Services subpackage map | Subpackage | Role | Key files | |---|---|---| | `db/` | SQLite substrate — source of truth for state/catalog/access/claims/requests/activity + transitions (audit). WAL single-writer, full-file bucket sync. | `connection.py` (txn primitives, `transaction()`, `get_writer`, `current_db_seq`), `migrate.py` (`PRAGMA user_version` runner over `migrations/NNNN_*.sql`), `sync.py` (bucket pull/push + CAS guard), `repo_state.py`, `repo_catalog.py`, `repo_access.py`, `repo_claims.py`, `repo_requests.py`, `repo_activity.py`, `repo_transitions.py`, `_serde.py`, `errors.py` | | `storage/` | Bucket/filesystem backend, per-reciter content IO, the cache registry, local auto-mount, external-file validation. | `cache.py` (all cache vars + invalidation), `data_dir.py` (slug→bytes/dict under `reciters//`), `storage_paths.py` (pure on-bucket path strings), `data_loader.py` (cached loaders for segs/verses/probe/reference), `hf_bucket.py` (`BucketBackend`/`FilesystemBackend`), `auto_mount.py` (local hf-mount FUSE), `bucket_audit.py` (Flask-free per-`reciters//` artefact validator against `qua_shared/schemas`; backs the per-slug CLI `scripts/bucket/audit_bucket_reciter.py`, the whole-bucket gate `scripts/diagnostics/validate_bucket.py`, and `/healthz?deep=1` via `sample_validation`) | | `audio/` | Everything bytes-on-disk → `