face-intel / docs /CONFIGURATION.md
Marwan
Restructure + add reverse face search (PimEyes-style)
f5eeb1c
|
Raw
History Blame Contribute Delete
31.8 kB
# 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.