File size: 31,824 Bytes
23d337e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 | # 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.
|