Configuration is managed through environment variables. Set these in:
.env file in the project root
Shell environment (export VAR=value)
Container environment (docker-compose.prod.yml service environment: blocks)
Priority: Environment > .env > Defaults (pydantic-settings resolution order). The settings classes read .env via env_file=".env" (no env_prefix on the main Settings class, so env vars are the uppercased field names; exceptions are noted per row).
Light enrichment service URL (pose/threat/pet/depth)
Note: In deployed containers all five router services are reached through the AI Gateway on ai-gateway:8090 — e.g. http://ai-gateway:8090/florence, /clip, /enrichment, /enrich-lt (set by docker-compose.prod.yml; .env.example ships the host-side equivalents on localhost:8090). The localhost:809x defaults above are the standalone-service dev ports. The standalone Nemotron LLM (compose service ai-llm) is the only AI service not routed through the gateway; the gateway routes /yolo26/clip/florence/enrichment/enrich-lt (ai/gateway/main.py).
Warning: Use HTTPS in production to prevent MITM attacks.
These control enrichment behaviors in the backend. Defaults are designed for “rich context”, but you can disable them if you’re resource constrained or running without those services.
Note:REID_SIMILARITY_THRESHOLD is tuned to the OSNet-AIN x1.0 person-vector space (512-d); the CLIP-era 0.85 would drop every legitimate OSNet match. The 0.7 default is PROVISIONAL pending calibration against real household galleries.
Max concurrent AI inference operations (20 on free-threaded Python)
Note: The default is resolved at startup by _get_default_inference_limit() in backend/core/config.py: 20 when running free-threaded Python (3.13t/3.14t with the GIL disabled), 4 on standard GIL builds.
llama.cpp parallel slots sharing the CTX_SIZE pool
Note:NEMOTRON_CONTEXT_WINDOW reads from the CTX_SIZE env var (validation_alias="CTX_SIZE" — single source of truth shared with llama.cpp). docker-compose.prod.yml passes CTX_SIZE=262144 (8 parallel slots x 32K) and PARALLEL=8 to both the ai-llm and backend containers — the backend block's defaults mirror the ai-llm service — so the backend divides the same pool by the same slot count. docker-compose.ghcr.yml passes CTX_SIZE=4096 / PARALLEL=2 to its backend: that file's ai-llm service sets only CTX_SIZE=4096, and the ghcr ai-llm image bakes PARALLEL=2 (ai/nemotron/Dockerfile) with no override passed.
Note:PARALLEL maps to llama_slot_count (validation_alias="PARALLEL") — llama.cpp's --parallel slot count. Slots share one CTX_SIZE pool, so the per-request budget above is derived as CTX_SIZE / PARALLEL.
High-confidence detections can bypass batching for immediate alerts.
Status: The fast path is DISABLED by default. The threshold ships at 2.0 (an impossible value > 1.0) and the object-type list ships empty, as a second safety net — fast path bypasses enrichment, which caused Nemotron to produce inaccurate scores (NEM-5525). Do not lower the threshold or add object types without first adding enrichment to the fast-path code path.
Variable
Required
Default
Range
Description
FAST_PATH_CONFIDENCE_THRESHOLD
No
2.0
0.0-10.0
Confidence threshold for fast path (2.0 = disabled)
When the queue reaches QUEUE_MAX_SIZE, the system applies the configured overflow policy:
Policy
Behavior
Use Case
dlq
Default. Move overflow items to dead-letter queue for later retry/inspection
Production - preserves all data for recovery
reject
Reject new items with an error; existing items preserved
Strict mode - alerts immediately on capacity
drop_oldest
Discard oldest items to make room for new ones
High-throughput - prioritizes recent detections
Consequences by policy:
dlq (default): Overflowed items are moved to dlq:detection_queue or dlq:analysis_queue. No data loss but requires monitoring DLQ depth. Use /api/dlq/stats to check and /api/dlq/requeue-all/{queue_name} to recover items.
reject: New detections are dropped when queue is full. Backend logs error Queue overflow: item rejected. Useful for alerting on capacity issues but causes immediate data loss.
drop_oldest: Oldest queued items are removed to make space. Prioritizes freshness over completeness. May result in missed security events from earlier timeframes.
Recommendation: Use dlq (default) for production deployments. Monitor hsi:queue:dlq:* Redis keys or the /api/dlq/stats endpoint to detect overflow conditions.
Reserved — not enforced (no code path reads it; see note)
Security: Admin endpoints are enabled by default for single-user local deployments; network binding to 127.0.0.1 is the primary security boundary. The require_admin_access dependency (backend/api/routes/admin.py) gates on ADMIN_ENABLED alone — the DEBUG=true pairing in the config.py comment is not enforced in the route code.
Log incoming HTTP requests (structured, with timing and correlation IDs)
REQUEST_LOGGING_BODY / REQUEST_LOGGING_HEADERS do not exist in the code — the request-logging layer (ObservabilityMiddleware, which merged RequestLoggingMiddleware in with NEM-5558) has no body/header options.
Record requests for replay/debugging (5xx always recorded)
REQUEST_RECORDING_SAMPLE_RATE
No
0.01
Fraction of successful requests sampled for recording (0.0-1.0)
REQUEST_RECORDING_MAX_BODY_SIZE
No
10000
Max request/response body to record in bytes (truncated above)
The recordings directory is not env-configurable — REQUEST_RECORDING_DIR does not exist; recordings land in data/recordings (DEFAULT_RECORDINGS_DIR, a constructor argument of RequestRecorderMiddleware).
Only HSTS_PRELOAD is an env variable (config field passed to SecurityHeadersMiddleware in backend/main.py). The HSTS header itself is always sent on HTTPS responses with constructor defaults max-age=31536000 (1 year) and includeSubDomains=true — HSTS_ENABLED, HSTS_MAX_AGE and HSTS_INCLUDE_SUBDOMAINS are middleware constructor arguments, not env-configurable.
Enable background model evaluation when GPU is idle
BACKGROUND_EVALUATION_GPU_IDLE_THRESHOLD
No
20
0-100
GPU utilization % at or below which the GPU counts as idle
BACKGROUND_EVALUATION_IDLE_DURATION
No
5
1-300
Seconds GPU must stay idle before evaluation starts
BACKGROUND_EVALUATION_POLL_INTERVAL
No
5.0
1-60s
How often (seconds) to check evaluation conditions
BACKGROUND_EVAL_ENABLED/BACKGROUND_EVAL_INTERVAL do not exist under those names. Evaluation is idle-gated, not fixed-interval: the documented "3600 s interval" had no counterpart in code — the closest knob, BACKGROUND_EVALUATION_POLL_INTERVAL, is the 5 s condition check.
Configuration for periodic cleanup of orphaned files (files on disk without corresponding database records).
Variable
Required
Default
Range
Description
ORPHAN_CLEANUP_ENABLED
No
true
-
Enable periodic cleanup of orphaned files
ORPHAN_CLEANUP_SCAN_INTERVAL_HOURS
No
24
1-168
Hours between cleanup scans (default: daily)
ORPHAN_CLEANUP_AGE_THRESHOLD_HOURS
No
24
1-720
Minimum age (hours) before an orphaned file can be deleted
Safety: Files younger than ORPHAN_CLEANUP_AGE_THRESHOLD_HOURS are skipped to allow for incomplete processing. This prevents deletion of files that may still be in use.
The VITE_* variables are embedded at frontend build time; the FRONTEND_* port variables are consumed by docker-compose.prod.yml at deploy time:
Variable
Required
Default
Description
VITE_API_BASE_URL
No
http://localhost:8000
Backend API URL
VITE_WS_BASE_URL
No
ws://localhost:8000
WebSocket URL
FRONTEND_HTTP_PORT
No
8080
Host port mapped to frontend nginx HTTP
FRONTEND_HTTPS_PORT
No
8444
Host port mapped to frontend nginx HTTPS
FRONTEND_INTERNAL_PORT
No
8080
Container port for nginx (health checks)
FRONTEND_PORT=5173 is dead in docker-compose.prod.yml — the compose file never references it (5173 only survives as the Vite dev-server target in frontend/Dockerfile). It is still consumed by scripts/test-docker.sh and a setup.py port-scanner entry, but it does not affect deployed frontend ports.