| # Face Intel — Configuration Guide |
|
|
| This document is the complete reference for every configuration knob |
| in Face Intel. All settings are environment-driven via |
| [pydantic-settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) |
| with the `FI_` prefix. |
|
|
| > **Source of truth:** [`config/settings.py`](../config/settings.py). |
| > This doc is generated from that file — if you add a setting there, |
| > add a row here too. |
|
|
| --- |
|
|
| ## Table of Contents |
|
|
| 1. [How Settings Work](#1-how-settings-work) |
| 2. [Quick Reference: All Settings](#2-quick-reference-all-settings) |
| 3. [Core](#3-core) |
| 4. [Provider Enable Flags](#4-provider-enable-flags) |
| 5. [Detection Tuning](#5-detection-tuning) |
| 6. [Recognition Tuning](#6-recognition-tuning) |
| 7. [Scraping](#7-scraping) |
| 8. [Reverse Image Search](#8-reverse-image-search) |
| 9. [Orchestrator](#9-orchestrator) |
| 10. [Cache](#10-cache) |
| 11. [Health & Circuit Breaker](#11-health--circuit-breaker) |
| 12. [Storage](#12-storage) |
| 13. [API](#13-api) |
| 14. [New-Capability Enable Flags](#14-new-capability-enable-flags) |
| 15. [Logging](#15-logging) |
| 16. [How to Enable/Disable Providers](#16-how-to-enabledisable-providers) |
| 17. [How to Configure API Keys](#17-how-to-configure-api-keys) |
| 18. [Production vs Development](#18-production-vs-development) |
| 19. [Verifying Your Configuration](#19-verifying-your-configuration) |
|
|
| --- |
|
|
| ## 1. How Settings Work |
|
|
| ### The `FI_` prefix |
| |
| Every field on the `Settings` class is automatically mapped to an |
| environment variable by uppercasing the field name and prepending |
| `FI_`. Examples: |
|
|
| | Field | Env var | |
| |---|---| |
| | `enable_haar` | `FI_ENABLE_HAAR` | |
| | `dnn_confidence_threshold` | `FI_DNN_CONFIDENCE_THRESHOLD` | |
| | `serpapi_key` | `FI_SERPAPI_KEY` | |
| | `cache_ttl_seconds` | `FI_CACHE_TTL_SECONDS` | |
|
|
| Env var names are **case-insensitive** (`case_sensitive=False`). |
|
|
| ### Loading order |
|
|
| pydantic-settings loads values in this order (later wins): |
|
|
| 1. Field defaults declared in `config/settings.py`. |
| 2. Values from the `.env` file at the project root (if present). |
| 3. Actual environment variables. |
|
|
| ### The `.env` file |
|
|
| Copy `.env.example` to `.env` and edit: |
|
|
| ```bash |
| cp .env.example .env |
| ``` |
|
|
| The `.env` file is **not** committed (it's in `.gitignore`). Each |
| line is `KEY=value`. Comments start with `#`. |
|
|
| ### Programmatic overrides |
|
|
| For tests or scripted runs, use the `make_settings()` factory: |
|
|
| ```python |
| from config.settings import make_settings |
| |
| settings = make_settings( |
| environment="test", |
| cache_enabled=False, |
| enable_dnn=False, |
| db_path=":memory:", |
| ) |
| ``` |
|
|
| This is exactly what `tests/conftest.py` does. |
|
|
| ### The `settings` singleton |
|
|
| `config/settings.py` exports a module-level `settings = Settings()` |
| instance. **Read-only consumers** (like provider tuning knobs) may |
| import it directly. **Stateful services** (Database, Cache, |
| Orchestrator, services) MUST receive their dependencies via |
| constructor injection — they never import this module. The DI |
| container at [`api/container.py`](../api/container.py) is the |
| composition root. |
|
|
| --- |
|
|
| ## 2. Quick Reference: All Settings |
|
|
| | Setting | Env var | Type | Default | Section | |
| |---|---|---|---|---| |
| | `app_name` | `FI_APP_NAME` | str | `"Face Intel"` | Core | |
| | `app_version` | `FI_APP_VERSION` | str | `"1.0.0"` | Core | |
| | `environment` | `FI_ENVIRONMENT` | str | `"development"` | Core | |
| | `host` | `FI_HOST` | str | `"0.0.0.0"` | Core | |
| | `port` | `FI_PORT` | int | `8000` | Core | |
| | `debug` | `FI_DEBUG` | bool | `False` | Core | |
| | `enable_haar` | `FI_ENABLE_HAAR` | bool | `True` | Providers | |
| | `enable_dnn` | `FI_ENABLE_DNN` | bool | `True` | Providers | |
| | `enable_mtcnn` | `FI_ENABLE_MTCNN` | bool | `True` | Providers | |
| | `enable_retinaface` | `FI_ENABLE_RETINAFACE` | bool | `False` | Providers | |
| | `enable_face_recognition` | `FI_ENABLE_FACE_RECOGNITION` | bool | `True` | Providers | |
| | `enable_deepface` | `FI_ENABLE_DEEPFACE` | bool | `False` | Providers | |
| | `enable_insightface` | `FI_ENABLE_INSIGHTFACE` | bool | `False` | Providers | |
| | `enable_beautifulsoup_scraper` | `FI_ENABLE_BEAUTIFULSOUP_SCRAPER` | bool | `True` | Providers | |
| | `enable_selenium_scraper` | `FI_ENABLE_SELENIUM_SCRAPER` | bool | `True` | Providers | |
| | `enable_bing_scraper` | `FI_ENABLE_BING_SCRAPER` | bool | `False` | Providers | |
| | `enable_duckduckgo_scraper` | `FI_ENABLE_DUCKDUCKGO_SCRAPER` | bool | `True` | Providers | |
| | `enable_google_lens` | `FI_ENABLE_GOOGLE_LENS` | bool | `True` | Providers | |
| | `enable_serpapi` | `FI_ENABLE_SERPAPI` | bool | `False` | Providers | |
| | `enable_yandex` | `FI_ENABLE_YANDEX` | bool | `False` | Providers | |
| | `enable_tineye` | `FI_ENABLE_TINEYE` | bool | `False` | Providers | |
| | `dnn_confidence_threshold` | `FI_DNN_CONFIDENCE_THRESHOLD` | float | `0.7` | Detection | |
| | `mtcnn_min_face_size` | `FI_MTCNN_MIN_FACE_SIZE` | int | `20` | Detection | |
| | `haar_scale_factor` | `FI_HAAR_SCALE_FACTOR` | float | `1.1` | Detection | |
| | `haar_min_neighbors` | `FI_HAAR_MIN_NEIGHBORS` | int | `5` | Detection | |
| | `face_recognition_tolerance` | `FI_FACE_RECOGNITION_TOLERANCE` | float | `0.6` | Recognition | |
| | `face_recognition_model` | `FI_FACE_RECOGNITION_MODEL` | str | `"hog"` | Recognition | |
| | `deepface_backend` | `FI_DEEPFACE_BACKEND` | str | `"arcface"` | Recognition | |
| | `insightface_model_pack` | `FI_INSIGHTFACE_MODEL_PACK` | str | `"buffalo_l"` | Recognition | |
| | `recognition_match_threshold` | `FI_RECOGNITION_MATCH_THRESHOLD` | float | `0.5` | Recognition | |
| | `scrape_timeout` | `FI_SCRAPE_TIMEOUT` | int | `30` | Scraping | |
| | `scrape_max_images` | `FI_SCRAPE_MAX_IMAGES` | int | `50` | Scraping | |
| | `selenium_headless` | `FI_SELENIUM_HEADLESS` | bool | `True` | Scraping | |
| | `selenium_implicit_wait` | `FI_SELENIUM_IMPLICIT_WAIT` | int | `10` | Scraping | |
| | `selenium_scroll_iterations` | `FI_SELENIUM_SCROLL_ITERATIONS` | int | `5` | Scraping | |
| | `user_agent` | `FI_USER_AGENT` | str | (Chrome 121 string) | Scraping | |
| | `reverse_search_max_results` | `FI_REVERSE_SEARCH_MAX_RESULTS` | int | `20` | Reverse | |
| | `serpapi_key` | `FI_SERPAPI_KEY` | str | `""` | Reverse | |
| | `bing_api_key` | `FI_BING_API_KEY` | str | `""` | Reverse | |
| | `tineye_public_key` | `FI_TINEYE_PUBLIC_KEY` | str | `""` | Reverse | |
| | `tineye_private_key` | `FI_TINEYE_PRIVATE_KEY` | str | `""` | Reverse | |
| | `orchestrator_timeout_seconds` | `FI_ORCHESTRATOR_TIMEOUT_SECONDS` | float | `90.0` | Orchestrator | |
| | `orchestrator_max_concurrency` | `FI_ORCHESTRATOR_MAX_CONCURRENCY` | int | `8` | Orchestrator | |
| | `retry_max_attempts` | `FI_RETRY_MAX_ATTEMPTS` | int | `3` | Orchestrator | |
| | `retry_initial_backoff_seconds` | `FI_RETRY_INITIAL_BACKOFF_SECONDS` | float | `0.5` | Orchestrator | |
| | `retry_max_backoff_seconds` | `FI_RETRY_MAX_BACKOFF_SECONDS` | float | `8.0` | Orchestrator | |
| | `cache_enabled` | `FI_CACHE_ENABLED` | bool | `True` | Cache | |
| | `cache_ttl_seconds` | `FI_CACHE_TTL_SECONDS` | int | `3600` | Cache | |
| | `cache_max_entries` | `FI_CACHE_MAX_ENTRIES` | int | `1000` | Cache | |
| | `health_check_interval_seconds` | `FI_HEALTH_CHECK_INTERVAL_SECONDS` | int | `60` | Health | |
| | `circuit_breaker_failure_threshold` | `FI_CIRCUIT_BREAKER_FAILURE_THRESHOLD` | int | `5` | Health | |
| | `circuit_breaker_recovery_seconds` | `FI_CIRCUIT_BREAKER_RECOVERY_SECONDS` | int | `120` | Health | |
| | `db_path` | `FI_DB_PATH` | str | `data/face_intel.db` | Storage | |
| | `audit_log_path` | `FI_AUDIT_LOG_PATH` | str | `data/audit.log` | Storage | |
| | `job_retention_days` | `FI_JOB_RETENTION_DAYS` | int | `7` | Storage | |
| | `rate_limit_per_minute` | `FI_RATE_LIMIT_PER_MINUTE` | int | `30` | API | |
| | `cors_origins` | `FI_CORS_ORIGINS` | list[str] | `["*"]` | API | |
| | `require_consent_header` | `FI_REQUIRE_CONSENT_HEADER` | bool | `True` | API | |
| | `consent_header_name` | `FI_CONSENT_HEADER_NAME` | str | `"X-Consent-Statement"` | API | |
| | `max_image_bytes` | `FI_MAX_IMAGE_BYTES` | int | `20971520` (20 MB) | API | |
| | `job_timeout_seconds` | `FI_JOB_TIMEOUT_SECONDS` | float | `300.0` | API | |
| | `max_request_body_bytes` | `FI_MAX_REQUEST_BODY_BYTES` | int | `26214400` (25 MB) | API | |
| | `enable_image_quality` | `FI_ENABLE_IMAGE_QUALITY` | bool | `True` | New caps | |
| | `enable_image_properties` | `FI_ENABLE_IMAGE_PROPERTIES` | bool | `True` | New caps | |
| | `enable_visual_features` | `FI_ENABLE_VISUAL_FEATURES` | bool | `False` | New caps | |
| | `enable_exif` | `FI_ENABLE_EXIF` | bool | `True` | New caps | |
| | `enable_xmp` | `FI_ENABLE_XMP` | bool | `False` | New caps | |
| | `enable_image_integrity` | `FI_ENABLE_IMAGE_INTEGRITY` | bool | `True` | New caps | |
| | `enable_duplicate_detector` | `FI_ENABLE_DUPLICATE_DETECTOR` | bool | `True` | New caps | |
| | `enable_manipulation_analyzer` | `FI_ENABLE_MANIPULATION_ANALYZER` | bool | `False` | New caps | |
| | `log_level` | `FI_LOG_LEVEL` | str | `"INFO"` | Logging | |
| | `log_json` | `FI_LOG_JSON` | bool | `False` | Logging | |
|
|
| --- |
|
|
| ## 3. Core |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `app_name` | `FI_APP_NAME` | str | `"Face Intel"` | Display name shown in OpenAPI title and `/` root. | |
| | `app_version` | `FI_APP_VERSION` | str | `"1.0.0"` | Version string exposed in `/health/providers` and OpenAPI. | |
| | `environment` | `FI_ENVIRONMENT` | str | `"development"` | Free-form label (`development`, `staging`, `production`, `test`). Currently informational only. | |
| | `host` | `FI_HOST` | str | `"0.0.0.0"` | Bind address for `uvicorn.run()` in `app.py`. | |
| | `port` | `FI_PORT` | int | `8000` | Bind port. | |
| | `debug` | `FI_DEBUG` | bool | `False` | If `True`, uvicorn runs with `--reload` (auto-restart on code changes). Never enable in production. | |
|
|
| ### Example |
|
|
| ```env |
| FI_ENVIRONMENT=production |
| FI_HOST=0.0.0.0 |
| FI_PORT=8000 |
| FI_DEBUG=false |
| ``` |
|
|
| --- |
|
|
| ## 4. Provider Enable Flags |
|
|
| Each provider has a `enable_<name>` flag. Default values follow this |
| rule: |
|
|
| - **Default `True`** for providers that ship with the platform and |
| have no external dependencies: `haar`, `dnn`, `mtcnn`, |
| `face_recognition`, `beautifulsoup_scraper`, `selenium_scraper`, |
| `duckduckgo_scraper`, `google_lens`. |
| - **Default `False`** for providers that require paid API keys or |
| heavy optional dependencies: `retinaface`, `deepface`, |
| `insightface`, `bing_scraper`, `serpapi`, `yandex`, `tineye`. |
|
|
| | Setting | Env var | Default | Capability | |
| |---|---|---|---| |
| | `enable_haar` | `FI_ENABLE_HAAR` | `True` | detection | |
| | `enable_dnn` | `FI_ENABLE_DNN` | `True` | detection | |
| | `enable_mtcnn` | `FI_ENABLE_MTCNN` | `True` | detection | |
| | `enable_retinaface` | `FI_ENABLE_RETINAFACE` | `False` | detection | |
| | `enable_face_recognition` | `FI_ENABLE_FACE_RECOGNITION` | `True` | recognition | |
| | `enable_deepface` | `FI_ENABLE_DEEPFACE` | `False` | recognition | |
| | `enable_insightface` | `FI_ENABLE_INSIGHTFACE` | `False` | recognition | |
| | `enable_beautifulsoup_scraper` | `FI_ENABLE_BEAUTIFULSOUP_SCRAPER` | `True` | scraping | |
| | `enable_selenium_scraper` | `FI_ENABLE_SELENIUM_SCRAPER` | `True` | scraping | |
| | `enable_bing_scraper` | `FI_ENABLE_BING_SCRAPER` | `False` | scraping | |
| | `enable_duckduckgo_scraper` | `FI_ENABLE_DUCKDUCKGO_SCRAPER` | `True` | scraping | |
| | `enable_google_lens` | `FI_ENABLE_GOOGLE_LENS` | `True` | reverse_search | |
| | `enable_serpapi` | `FI_ENABLE_SERPAPI` | `False` | reverse_search | |
| | `enable_yandex` | `FI_ENABLE_YANDEX` | `False` | reverse_search | |
| | `enable_tineye` | `FI_ENABLE_TINEYE` | `False` | reverse_search | |
| |
| ### Notes |
| |
| - A flag set to `True` does not guarantee the provider is `healthy` — |
| the provider's `is_available()` may still return `False` if an |
| optional dependency is missing or an API key is not set. In that |
| case it shows up as `not_configured` in `/providers`. |
| - See [§16](#16-how-to-enabledisable-providers) for the workflow. |
|
|
| --- |
|
|
| ## 5. Detection Tuning |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `dnn_confidence_threshold` | `FI_DNN_CONFIDENCE_THRESHOLD` | float | `0.7` | Minimum confidence (0–1) for the DNN Caffe SSD detector to accept a detection. Lower = more faces (more false positives). | |
| | `mtcnn_min_face_size` | `FI_MTCNN_MIN_FACE_SIZE` | int | `20` | Minimum face size in pixels for MTCNN. Increase for speed on high-resolution images. | |
| | `haar_scale_factor` | `FI_HAAR_SCALE_FACTOR` | float | `1.1` | Scale factor for Haar Cascade multiscale detection. Lower = more thorough, slower. | |
| | `haar_min_neighbors` | `FI_HAAR_MIN_NEIGHBORS` | int | `5` | Min neighbors for Haar group rect merging. Higher = fewer detections, more confident. | |
|
|
| ### Example: tuning for accuracy |
|
|
| ```env |
| FI_DNN_CONFIDENCE_THRESHOLD=0.5 |
| FI_MTCNN_MIN_FACE_SIZE=40 |
| FI_HAAR_SCALE_FACTOR=1.05 |
| FI_HAAR_MIN_NEIGHBORS=8 |
| ``` |
|
|
| ### Example: tuning for speed |
|
|
| ```env |
| FI_DNN_CONFIDENCE_THRESHOLD=0.9 |
| FI_MTCNN_MIN_FACE_SIZE=80 |
| FI_HAAR_SCALE_FACTOR=1.3 |
| FI_HAAR_MIN_NEIGHBORS=3 |
| ``` |
|
|
| --- |
|
|
| ## 6. Recognition Tuning |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `face_recognition_tolerance` | `FI_FACE_RECOGNITION_TOLERANCE` | float | `0.6` | dlib distance tolerance for `face_recognition` provider. Lower = stricter (fewer false positives). | |
| | `face_recognition_model` | `FI_FACE_RECOGNITION_MODEL` | str | `"hog"` | dlib model: `"hog"` (CPU, fast) or `"cnn"` (CUDA, accurate). | |
| | `deepface_backend` | `FI_DEEPFACE_BACKEND` | str | `"arcface"` | DeepFace backend: `arcface`, `facenet`, `vggface`, `openface`, etc. | |
| | `insightface_model_pack` | `FI_INSIGHTFACE_MODEL_PACK` | str | `"buffalo_l"` | InsightFace model pack name. | |
| | `recognition_match_threshold` | `FI_RECOGNITION_MATCH_THRESHOLD` | float | `0.5` | Generic similarity threshold for considering a face "matched". | |
|
|
| ### Example: stricter recognition |
|
|
| ```env |
| FI_FACE_RECOGNITION_TOLERANCE=0.4 |
| FI_RECOGNITION_MATCH_THRESHOLD=0.65 |
| ``` |
|
|
| --- |
|
|
| ## 7. Scraping |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `scrape_timeout` | `FI_SCRAPE_TIMEOUT` | int | `30` | HTTP timeout in seconds for scraper requests. | |
| | `scrape_max_images` | `FI_SCRAPE_MAX_IMAGES` | int | `50` | Maximum images to extract from a single page. | |
| | `selenium_headless` | `FI_SELENIUM_HEADLESS` | bool | `True` | Run Chrome/Chromium headless. Set `False` for debugging. | |
| | `selenium_implicit_wait` | `FI_SELENIUM_IMPLICIT_WAIT` | int | `10` | Selenium implicit wait seconds. | |
| | `selenium_scroll_iterations` | `FI_SELENIUM_SCROLL_ITERATIONS` | int | `5` | Number of times to scroll the page (loads lazy images). | |
| | `user_agent` | `FI_USER_AGENT` | str | (Chrome 121 string) | User-Agent header for HTTP requests and Selenium. | |
|
|
| ### Example: scraping authenticated intranet pages |
|
|
| ```env |
| FI_USER_AGENT="MyCorp Internal Bot/1.0 (contact: ops@mycorp.example)" |
| FI_SELENIUM_HEADLESS=true |
| FI_SELENIUM_IMPLICIT_WAIT=20 |
| FI_SELENIUM_SCROLL_ITERATIONS=10 |
| FI_SCRAPE_MAX_IMAGES=200 |
| ``` |
|
|
| --- |
|
|
| ## 8. Reverse Image Search |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `reverse_search_max_results` | `FI_REVERSE_SEARCH_MAX_RESULTS` | int | `20` | Max results to return per reverse-search provider. | |
| | `serpapi_key` | `FI_SERPAPI_KEY` | str | `""` | SerpAPI API key. Required for `serpapi` provider to be `available`. | |
| | `bing_api_key` | `FI_BING_API_KEY` | str | `""` | Bing Image Search API key. Required for `bing` scraper. | |
| | `tineye_public_key` | `FI_TINEYE_PUBLIC_KEY` | str | `""` | TinEye OAuth public key. | |
| | `tineye_private_key` | `FI_TINEYE_PRIVATE_KEY` | str | `""` | TinEye OAuth private key. | |
|
|
| ### Example: enabling SerpAPI |
|
|
| ```env |
| FI_ENABLE_SERPAPI=true |
| FI_SERPAPI_KEY=your_serpapi_key_here |
| FI_REVERSE_SEARCH_MAX_RESULTS=50 |
| ``` |
|
|
| See [§17](#17-how-to-configure-api-keys) for the full key setup |
| workflow. |
|
|
| --- |
|
|
| ## 9. Orchestrator |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `orchestrator_timeout_seconds` | `FI_ORCHESTRATOR_TIMEOUT_SECONDS` | float | `90.0` | Per-provider hard timeout. If a single provider takes longer, the orchestrator cancels it and records a `TimeoutError` result. | |
| | `orchestrator_max_concurrency` | `FI_ORCHESTRATOR_MAX_CONCURRENCY` | int | `8` | Max number of providers to invoke concurrently. Lower this on memory-constrained hosts. | |
| | `retry_max_attempts` | `FI_RETRY_MAX_ATTEMPTS` | int | `3` | Max retry attempts per provider invocation (including the first). | |
| | `retry_initial_backoff_seconds` | `FI_RETRY_INITIAL_BACKOFF_SECONDS` | float | `0.5` | Initial backoff. Doubles each attempt up to `retry_max_backoff_seconds`, with full jitter. | |
| | `retry_max_backoff_seconds` | `FI_RETRY_MAX_BACKOFF_SECONDS` | float | `8.0` | Upper bound on backoff delay. | |
|
|
| ### Retriable exceptions |
|
|
| Only `TimeoutError`, `ConnectionError`, and `OSError` are retried. |
| Other exceptions (e.g. `RuntimeError` from a bad API response) are |
| not retried — see [`docs/PROVIDERS.md` §10](PROVIDERS.md#10-error-handling-pattern-_safe_execute) |
| for how to convert transient errors into retriable ones. |
|
|
| ### Example: aggressive retry for flaky networks |
|
|
| ```env |
| FI_RETRY_MAX_ATTEMPTS=5 |
| FI_RETRY_INITIAL_BACKOFF_SECONDS=1.0 |
| FI_RETRY_MAX_BACKOFF_SECONDS=30.0 |
| FI_ORCHESTRATOR_TIMEOUT_SECONDS=180 |
| FI_ORCHESTRATOR_MAX_CONCURRENCY=4 |
| ``` |
|
|
| ### Example: fast-fail for low-latency APIs |
|
|
| ```env |
| FI_RETRY_MAX_ATTEMPTS=1 |
| FI_ORCHESTRATOR_TIMEOUT_SECONDS=15 |
| FI_ORCHESTRATOR_MAX_CONCURRENCY=16 |
| ``` |
|
|
| --- |
|
|
| ## 10. Cache |
|
|
| The cache stores successful `ProviderResult` objects keyed by |
| `f"{provider_name}:{image_hash}"`. On a cache hit, the provider is |
| not invoked — the cached result is returned (with `metadata.cache_hit=True`). |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `cache_enabled` | `FI_CACHE_ENABLED` | bool | `True` | Master switch. Set `False` to disable cache entirely (every request invokes providers). | |
| | `cache_ttl_seconds` | `FI_CACHE_TTL_SECONDS` | int | `3600` | Time-to-live for cache entries (seconds). Entries expire even if not evicted by LRU. | |
| | `cache_max_entries` | `FI_CACHE_MAX_ENTRIES` | int | `1000` | Maximum entries. When exceeded, the oldest entry is evicted (LRU). | |
|
|
| ### Cache eviction |
|
|
| Two policies compose: |
|
|
| - **TTL:** `cache_ttl_seconds` after `set()`, the entry expires. |
| - **LRU:** when adding a new entry would exceed `cache_max_entries`, |
| the least-recently-accessed entry is evicted. Eviction counter is |
| exposed in `GET /cache` as `evictions`. |
|
|
| ### Example: development (always-fresh) |
|
|
| ```env |
| FI_CACHE_ENABLED=false |
| ``` |
|
|
| ### Example: high-throughput production |
|
|
| ```env |
| FI_CACHE_ENABLED=true |
| FI_CACHE_TTL_SECONDS=86400 |
| FI_CACHE_MAX_ENTRIES=10000 |
| ``` |
|
|
| ### Inspecting the cache |
|
|
| ```bash |
| curl http://localhost:8000/cache |
| curl -X DELETE http://localhost:8000/cache |
| ``` |
|
|
| See [`docs/API_REFERENCE.md` §9](API_REFERENCE.md#9-cache-endpoints). |
|
|
| --- |
|
|
| ## 11. Health & Circuit Breaker |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `health_check_interval_seconds` | `FI_HEALTH_CHECK_INTERVAL_SECONDS` | int | `60` | Reserved for future background health polling (currently informational). | |
| | `circuit_breaker_failure_threshold` | `FI_CIRCUIT_BREAKER_FAILURE_THRESHOLD` | int | `5` | Consecutive failures before the circuit opens for a provider. | |
| | `circuit_breaker_recovery_seconds` | `FI_CIRCUIT_BREAKER_RECOVERY_SECONDS` | int | `120` | Seconds before an open circuit transitions to half-open (allows one trial call). | |
|
|
| ### Circuit breaker lifecycle |
|
|
| 1. Provider fails → `consecutive_failures += 1`. |
| 2. When `consecutive_failures >= circuit_breaker_failure_threshold`, |
| the circuit **opens** and the orchestrator skips this provider. |
| 3. After `circuit_breaker_recovery_seconds` elapses, the circuit |
| transitions to **half-open**: the next request is allowed through. |
| 4. If the trial call succeeds, the circuit **closes** and |
| `consecutive_failures` resets to 0. |
| 5. If the trial call fails, the circuit **re-opens** for another |
| recovery window. |
|
|
| ### Example: aggressive failover |
|
|
| ```env |
| FI_CIRCUIT_BREAKER_FAILURE_THRESHOLD=3 |
| FI_CIRCUIT_BREAKER_RECOVERY_SECONDS=30 |
| ``` |
|
|
| ### Example: tolerant of transient blips |
|
|
| ```env |
| FI_CIRCUIT_BREAKER_FAILURE_THRESHOLD=10 |
| FI_CIRCUIT_BREAKER_RECOVERY_SECONDS=300 |
| ``` |
|
|
| ### Inspecting circuit state |
|
|
| ```bash |
| curl http://localhost:8000/health/providers | jq '.providers[] | {name, circuit_open, consecutive_failures}' |
| ``` |
|
|
| --- |
|
|
| ## 12. Storage |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `db_path` | `FI_DB_PATH` | str | `data/face_intel.db` | SQLite database path. Use `:memory:` for in-memory (tests). | |
| | `audit_log_path` | `FI_AUDIT_LOG_PATH` | str | `data/audit.log` | JSONL audit log path. Appended to on sensitive operations. | |
| | `job_retention_days` | `FI_JOB_RETENTION_DAYS` | int | `7` | Days to keep completed jobs. Older jobs are deleted by `Database.cleanup_old_jobs()` (call from a cron/scheduler). | |
|
|
| ### Storage directory layout |
|
|
| The settings module auto-creates these directories on import: |
|
|
| ``` |
| data/ |
| ├── face_intel.db # SQLite database (jobs, results) |
| ├── audit.log # JSONL audit trail |
| ├── models/ # Auto-downloaded model files (DNN Caffe, etc.) |
| ├── gallery/ # Known-faces reference store |
| │ ├── manifest.json |
| │ └── alice_0.npy # Per-person embeddings |
| ├── uploads/ # Source images saved by ArtifactStore |
| └── generated/ # Annotated images, montages |
| ``` |
|
|
| ### Example: production paths |
|
|
| ```env |
| FI_DB_PATH=/var/lib/face-intel/face_intel.db |
| FI_AUDIT_LOG_PATH=/var/log/face-intel/audit.log |
| FI_JOB_RETENTION_DAYS=30 |
| ``` |
|
|
| ### Example: tests (in-memory) |
|
|
| ```python |
| make_settings(db_path=":memory:") |
| ``` |
|
|
| --- |
|
|
| ## 13. API |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `rate_limit_per_minute` | `FI_RATE_LIMIT_PER_MINUTE` | int | `30` | Max requests per client IP per 60s sliding window. `/health/*` is excluded. | |
| | `cors_origins` | `FI_CORS_ORIGINS` | list[str] | `["*"]` | Allowed CORS origins. Comma-separated in env: `FI_CORS_ORIGINS=["https://app.example.com","https://admin.example.com"]`. | |
| | `require_consent_header` | `FI_REQUIRE_CONSENT_HEADER` | bool | `True` | Whether consent is required (currently informational — enforce at your gateway). | |
| | `consent_header_name` | `FI_CONSENT_HEADER_NAME` | str | `"X-Consent-Statement"` | Header name to look for. | |
| | `max_image_bytes` | `FI_MAX_IMAGE_BYTES` | int | `20971520` (20 MB) | Max decoded image size, enforced by `InputValidator`. | |
| | `job_timeout_seconds` | `FI_JOB_TIMEOUT_SECONDS` | float | `300.0` | Overall job timeout. A job exceeding this is marked `timeout`. | |
| | `max_request_body_bytes` | `FI_MAX_REQUEST_BODY_BYTES` | int | `26214400` (25 MB) | Hard HTTP body cap. Returns `413` if exceeded. | |
|
|
| ### Example: locked-down production |
|
|
| ```env |
| FI_RATE_LIMIT_PER_MINUTE=120 |
| FI_CORS_ORIGINS=["https://app.example.com"] |
| FI_REQUIRE_CONSENT_HEADER=true |
| FI_MAX_IMAGE_BYTES=10485760 |
| FI_JOB_TIMEOUT_SECONDS=60 |
| FI_MAX_REQUEST_BODY_BYTES=12582912 |
| ``` |
|
|
| ### Example: permissive development |
|
|
| ```env |
| FI_RATE_LIMIT_PER_MINUTE=10000 |
| FI_CORS_ORIGINS=["*"] |
| FI_REQUIRE_CONSENT_HEADER=false |
| FI_DEBUG=true |
| ``` |
|
|
| --- |
|
|
| ## 14. New-Capability Enable Flags |
|
|
| These enable flags cover the non-detection/recognition capabilities |
| added in Phase 4+. |
|
|
| | Setting | Env var | Type | Default | Capability | |
| |---|---|---|---|---| |
| | `enable_image_quality` | `FI_ENABLE_IMAGE_QUALITY` | bool | `True` | image_analysis | |
| | `enable_image_properties` | `FI_ENABLE_IMAGE_PROPERTIES` | bool | `True` | image_analysis | |
| | `enable_visual_features` | `FI_ENABLE_VISUAL_FEATURES` | bool | `False` | image_analysis | |
| | `enable_exif` | `FI_ENABLE_EXIF` | bool | `True` | metadata | |
| | `enable_xmp` | `FI_ENABLE_XMP` | bool | `False` | metadata | |
| | `enable_image_integrity` | `FI_ENABLE_IMAGE_INTEGRITY` | bool | `True` | forensics | |
| | `enable_duplicate_detector` | `FI_ENABLE_DUPLICATE_DETECTOR` | bool | `True` | forensics | |
| | `enable_manipulation_analyzer` | `FI_ENABLE_MANIPULATION_ANALYZER` | bool | `False` | forensics | |
|
|
| --- |
|
|
| ## 15. Logging |
|
|
| | Setting | Env var | Type | Default | Description | |
| |---|---|---|---|---| |
| | `log_level` | `FI_LOG_LEVEL` | str | `"INFO"` | Loguru level: `TRACE`, `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`. | |
| | `log_json` | `FI_LOG_JSON` | bool | `False` | Emit newline-delimited JSON for log aggregators (Loki, Datadog, CloudWatch). | |
|
|
| ### Log format |
|
|
| **Default (human-readable):** |
|
|
| ``` |
| 2026-07-10 14:30:00.123 | INFO | eid=abc123def456 | pid=haar | retry=0 | status=started | invoking haar |
| ``` |
|
|
| **JSON mode (`FI_LOG_JSON=true`):** |
|
|
| ```json |
| {"timestamp":"2026-07-10T14:30:00.123Z","level":"INFO","execution_id":"abc123def456","provider_id":"haar","retry_count":0,"status":"started","message":"invoking haar"} |
| ``` |
|
|
| Every log line inside an execution carries `eid` (execution id), |
| `pid` (provider id), `retry`, and `status` fields. See |
| [`docs/ARCHITECTURE.md` §7](ARCHITECTURE.md) for details. |
|
|
| ### Example: production JSON logging |
|
|
| ```env |
| FI_LOG_LEVEL=INFO |
| FI_LOG_JSON=true |
| ``` |
|
|
| ### Example: debug verbosity |
|
|
| ```env |
| FI_LOG_LEVEL=DEBUG |
| FI_LOG_JSON=false |
| ``` |
|
|
| --- |
|
|
| ## 16. How to Enable/Disable Providers |
|
|
| ### Workflow |
|
|
| 1. Edit `.env` (or set the env var directly). |
| 2. Set `FI_ENABLE_<NAME>=true` (or `false`). |
| 3. Restart the server. |
| 4. Verify via `GET /providers`. |
|
|
| ### Example: enable `dnn` + `mtcnn`, disable `haar` |
|
|
| ```env |
| FI_ENABLE_HAAR=false |
| FI_ENABLE_DNN=true |
| FI_ENABLE_MTCNN=true |
| ``` |
|
|
| Restart and check: |
|
|
| ```bash |
| curl -s http://localhost:8000/providers | jq '.providers[] | {name, status}' |
| ``` |
|
|
| ### What "disabled" vs "not_configured" means |
| |
| | Status | Cause | |
| |---|---| |
| | `disabled` | `enable_<name> = False` in settings. The provider class is never even imported. | |
| | `not_configured` | `enable_<name> = True` but the optional dependency is missing OR `is_available()` returned `False` (e.g. API key not set). | |
| | `healthy` | Enabled, instantiated, `is_available() == True`. | |
|
|
| ### Disabling vs uninstalling |
|
|
| Disabling a provider via `enable_<name>=False` is enough — the |
| provider class won't be imported, so even broken optional |
| dependencies won't break startup. You don't need to uninstall the |
| package. |
|
|
| --- |
|
|
| ## 17. How to Configure API Keys |
|
|
| ### SerpAPI (Google Reverse Image Search) |
|
|
| 1. Sign up at <https://serpapi.com/>. |
| 2. Get your API key from the dashboard. |
| 3. Set in `.env`: |
|
|
| ```env |
| FI_ENABLE_SERPAPI=true |
| FI_SERPAPI_KEY=your_key_here |
| ``` |
|
|
| 4. Restart and verify: |
|
|
| ```bash |
| curl -s http://localhost:8000/providers/serpapi | jq '.available' |
| # → true |
| ``` |
|
|
| ### Bing Image Search API |
|
|
| 1. Provision a Bing Search v7 resource in Azure. |
| 2. Get the API key from "Keys and Endpoint". |
| 3. Set in `.env`: |
|
|
| ```env |
| FI_ENABLE_BING_SCRAPER=true |
| FI_BING_API_KEY=your_key_here |
| ``` |
|
|
| ### TinEye (OAuth) |
|
|
| TinEye uses public/private keypair authentication. |
|
|
| 1. Sign up at <https://tineye.com/developers>. |
| 2. Generate a keypair. |
| 3. Set in `.env`: |
|
|
| ```env |
| FI_ENABLE_TINEYE=true |
| FI_TINEYE_PUBLIC_KEY=your_public_key |
| FI_TINEYE_PRIVATE_KEY=your_private_key |
| ``` |
|
|
| ### Secret management |
|
|
| For production, **do not** store API keys in `.env`. Use your |
| orchestrator's secret store: |
|
|
| - **Kubernetes:** `Secret` mounted as env vars. |
| - **AWS:** Secrets Manager + entrypoint script that fetches and |
| exports. |
| - **HashiCorp Vault:** `vault kv get` in the entrypoint. |
|
|
| The platform reads keys from env vars only — it doesn't care how |
| they got there. |
|
|
| --- |
|
|
| ## 18. Production vs Development |
|
|
| ### Development profile |
|
|
| `.env.dev`: |
|
|
| ```env |
| FI_ENVIRONMENT=development |
| FI_DEBUG=true |
| FI_LOG_LEVEL=DEBUG |
| FI_LOG_JSON=false |
| FI_RATE_LIMIT_PER_MINUTE=10000 |
| FI_CORS_ORIGINS=["*"] |
| FI_REQUIRE_CONSENT_HEADER=false |
| FI_CACHE_ENABLED=false |
| FI_DB_PATH=data/face_intel.dev.db |
| |
| # Enable most providers for testing |
| FI_ENABLE_HAAR=true |
| FI_ENABLE_DNN=true |
| FI_ENABLE_MTCNN=true |
| FI_ENABLE_FACE_RECOGNITION=true |
| FI_ENABLE_BEAUTIFULSOUP_SCRAPER=true |
| FI_ENABLE_SELENIUM_SCRAPER=true |
| FI_ENABLE_DUCKDUCKGO_SCRAPER=true |
| FI_ENABLE_GOOGLE_LENS=true |
| FI_ENABLE_IMAGE_QUALITY=true |
| FI_ENABLE_IMAGE_PROPERTIES=true |
| FI_ENABLE_EXIF=true |
| FI_ENABLE_IMAGE_INTEGRITY=true |
| FI_ENABLE_DUPLICATE_DETECTOR=true |
| |
| # Disable paid/heavy providers |
| FI_ENABLE_RETINAFACE=false |
| FI_ENABLE_DEEPFACE=false |
| FI_ENABLE_INSIGHTFACE=false |
| FI_ENABLE_BING_SCRAPER=false |
| FI_ENABLE_SERPAPI=false |
| FI_ENABLE_YANDEX=false |
| FI_ENABLE_TINEYE=false |
| FI_ENABLE_VISUAL_FEATURES=false |
| FI_ENABLE_XMP=false |
| FI_ENABLE_MANIPULATION_ANALYZER=false |
| ``` |
|
|
| Run with: |
|
|
| ```bash |
| cp .env.dev .env |
| python app.py |
| ``` |
|
|
| ### Production profile |
|
|
| `.env.prod`: |
|
|
| ```env |
| FI_ENVIRONMENT=production |
| FI_DEBUG=false |
| FI_LOG_LEVEL=INFO |
| FI_LOG_JSON=true |
| FI_RATE_LIMIT_PER_MINUTE=120 |
| FI_CORS_ORIGINS=["https://app.example.com"] |
| FI_REQUIRE_CONSENT_HEADER=true |
| FI_CACHE_ENABLED=true |
| FI_CACHE_TTL_SECONDS=86400 |
| FI_CACHE_MAX_ENTRIES=10000 |
| FI_DB_PATH=/var/lib/face-intel/face_intel.db |
| FI_AUDIT_LOG_PATH=/var/log/face-intel/audit.log |
| FI_JOB_RETENTION_DAYS=30 |
| FI_JOB_TIMEOUT_SECONDS=120 |
| FI_MAX_IMAGE_BYTES=10485760 |
| FI_MAX_REQUEST_BODY_BYTES=12582912 |
| |
| # Circuit breaker — aggressive failover |
| FI_CIRCUIT_BREAKER_FAILURE_THRESHOLD=3 |
| FI_CIRCUIT_BREAKER_RECOVERY_SECONDS=60 |
| |
| # Orchestrator |
| FI_ORCHESTRATOR_TIMEOUT_SECONDS=60 |
| FI_ORCHESTRATOR_MAX_CONCURRENCY=16 |
| FI_RETRY_MAX_ATTEMPTS=3 |
| FI_RETRY_INITIAL_BACKOFF_SECONDS=1.0 |
| FI_RETRY_MAX_BACKOFF_SECONDS=15.0 |
| |
| # Providers — only the ones you've validated |
| FI_ENABLE_HAAR=true |
| FI_ENABLE_DNN=true |
| FI_ENABLE_FACE_RECOGNITION=true |
| FI_ENABLE_IMAGE_QUALITY=true |
| FI_ENABLE_IMAGE_PROPERTIES=true |
| FI_ENABLE_EXIF=true |
| FI_ENABLE_IMAGE_INTEGRITY=true |
| FI_ENABLE_DUPLICATE_DETECTOR=true |
| |
| # Disable everything else |
| FI_ENABLE_MTCNN=false |
| FI_ENABLE_RETINAFACE=false |
| FI_ENABLE_DEEPFACE=false |
| FI_ENABLE_INSIGHTFACE=false |
| FI_ENABLE_BEAUTIFULSOUP_SCRAPER=false |
| FI_ENABLE_SELENIUM_SCRAPER=false |
| FI_ENABLE_BING_SCRAPER=false |
| FI_ENABLE_DUCKDUCKGO_SCRAPER=false |
| FI_ENABLE_GOOGLE_LENS=false |
| FI_ENABLE_SERPAPI=false |
| FI_ENABLE_YANDEX=false |
| FI_ENABLE_TINEYE=false |
| FI_ENABLE_VISUAL_FEATURES=false |
| FI_ENABLE_XMP=false |
| FI_ENABLE_MANIPULATION_ANALYZER=false |
| ``` |
|
|
| ### Test profile (used by `tests/conftest.py`) |
|
|
| ```python |
| Settings( |
| environment="test", |
| enable_dnn=False, |
| enable_mtcnn=False, |
| # ... all optional providers disabled ... |
| enable_visual_features=False, |
| enable_xmp=False, |
| enable_manipulation_analyzer=False, |
| db_path=":memory:", |
| cache_enabled=False, |
| rate_limit_per_minute=10000, |
| ) |
| ``` |
|
|
| This keeps tests fast and deterministic — only pure-OpenCV providers |
| (`haar`, `image_quality`, `image_properties`, `exif`, |
| `image_integrity`, `duplicate_detector`) are enabled. |
|
|
| --- |
|
|
| ## 19. Verifying Your Configuration |
|
|
| ### 1. Check the loaded settings |
|
|
| ```bash |
| python -c "from config.settings import settings; print(settings.model_dump_json(indent=2))" |
| ``` |
|
|
| ### 2. Check provider status |
|
|
| ```bash |
| curl -s http://localhost:8000/providers | jq '.providers[] | {name, status, available}' |
| ``` |
|
|
| Expected output: |
|
|
| ```json |
| {"name": "haar", "status": "healthy", "available": true} |
| {"name": "dnn", "status": "healthy", "available": true} |
| {"name": "retinaface", "status": "disabled", "available": false} |
| {"name": "insightface", "status": "not_configured", "available": false} |
| ``` |
|
|
| ### 3. Check manifest errors |
|
|
| ```bash |
| curl -s http://localhost:8000/providers | jq '.errors' |
| ``` |
|
|
| If `errors` is non-empty, those providers failed to instantiate (e.g. |
| missing optional dependency): |
|
|
| ```json |
| { |
| "insightface": "missing dependency: No module named 'insightface'" |
| } |
| ``` |
|
|
| ### 4. Check health |
|
|
| ```bash |
| curl -s http://localhost:8000/health/providers | jq '.status' |
| # → "healthy" or "degraded" |
| ``` |
|
|
| ### 5. Check stats |
|
|
| ```bash |
| curl -s http://localhost:8000/stats | jq '.counters' |
| ``` |
|
|
| --- |
|
|
| ## See Also |
|
|
| - [`docs/API_REFERENCE.md`](API_REFERENCE.md) — what each setting |
| affects at the API level. |
| - [`docs/PROVIDERS.md`](PROVIDERS.md) — how to add new enable flags. |
| - [`docs/DEPLOYMENT.md`](DEPLOYMENT.md) — running in Docker / |
| Kubernetes, env-var injection. |
| - [`docs/TROUBLESHOOTING.md`](TROUBLESHOOTING.md) — diagnosing |
| misconfigured providers. |
|
|