# 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_` 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_=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_ = False` in settings. The provider class is never even imported. | | `not_configured` | `enable_ = 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_=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 . 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 . 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.