Administration Guide Configuration, secrets management, and security for Home Security Intelligence.
Table of Contents Configuration Overview Loading Order Configuration loads in this order (later sources override earlier):
Default values in backend/core/config.py .env file in project root data/runtime.env (runtime overrides) Environment variables (highest priority) Key Configuration Files File Purpose .env Local overrides (git ignored) .env.example Template with documented defaults data/runtime.env Runtime overrides (survives container rebuilds) backend/core/config.py Source of truth for all settings
Quick Setup # Interactive setup (recommended)
python setup.py # Quick mode
python setup.py --guided # Guided mode with explanations
# Manual setup
cp .env.example .env
# Edit .env with your settings
chmod 600 .env
Environment Variables Essential Variables # Database (REQUIRED - no default password)
DATABASE_URL = postgresql+asyncpg://security:<password>@postgres:5432/security
# Redis
REDIS_URL = redis://redis:6379
# AI Services (production - compose network)
USE_AI_GATEWAY = true
AI_GATEWAY_URL = http://ai-gateway:8090
NEMOTRON_URL = http://ai-llm:8091
YOLO26_URL = http://ai-gateway:8090/yolo26
FLORENCE_URL = http://ai-gateway:8090/florence
CLIP_URL = http://ai-gateway:8090/clip
ENRICHMENT_URL = http://ai-gateway:8090/enrichment
ENRICHMENT_LIGHT_URL = http://ai-gateway:8090/enrich-lt
# Camera uploads
FOSCAM_BASE_PATH = /export/foscam
# Retention
RETENTION_DAYS = 30
Application Settings Variable Default Description APP_NAME Home Security Intelligence Application display name APP_VERSION 0.1.0 Version string DEBUG false Enable debug mode ADMIN_ENABLED true Enable admin endpoints — the only gate; DEBUG is not consulted ADMIN_API_KEY none Reserved, not enforced — no code path reads it
Detection Settings Variable Default Range Description DETECTION_CONFIDENCE_THRESHOLD 0.5 0.0-1.0 Minimum confidence FAST_PATH_CONFIDENCE_THRESHOLD 0.90 0.0-1.0 Fast path threshold FAST_PATH_OBJECT_TYPES ["person"] JSON array Fast path object types
Batch Processing Variable Default Range Description BATCH_WINDOW_SECONDS 90 1-3600 Maximum batch duration BATCH_IDLE_TIMEOUT_SECONDS 30 1-3600 Close batch after idle
AI Service Timeouts Variable Default Range Description AI_CONNECT_TIMEOUT 10.0 1.0-60.0 Connection timeout (s) AI_HEALTH_TIMEOUT 5.0 1.0-30.0 Health check timeout (s) YOLO26_READ_TIMEOUT 30.0 5.0-120.0 Detection timeout (s) NEMOTRON_READ_TIMEOUT 120.0 30.0-600.0 LLM timeout (s)
GPU Monitoring Variable Default Range Description GPU_POLL_INTERVAL_SECONDS 5.0 1.0-60.0 GPU stats polling GPU_STATS_HISTORY_MINUTES 60 1-1440 In-memory history
Camera Integration Variable Default Description FOSCAM_BASE_PATH /export/foscam FTP upload directory FILE_WATCHER_POLLING false Use polling instead of native events FILE_WATCHER_POLLING_INTERVAL 1.0 Polling interval (seconds)
Secrets Management Docker Secrets (Recommended for Production) Docker Secrets provide enhanced security over environment variables:
Not visible in docker inspect output Mounted read-only in containers at /run/secrets/ Stored with restrictive permissions (600) Setup # Create secrets directory
mkdir -p secrets && chmod 700 secrets
# Generate secure passwords
openssl rand -base64 32 > secrets/postgres_password.txt
openssl rand -base64 32 > secrets/redis_password.txt
openssl rand -base64 32 > secrets/grafana_admin_password.txt
# Set permissions
chmod 600 secrets/*.txt
Enable in Docker Compose Uncomment the secrets sections in docker-compose.prod.yml:
# Bottom of docker-compose.prod.yml
secrets :
postgres_password :
file : ./secrets/postgres_password.txt
redis_password :
file : ./secrets/redis_password.txt
grafana_admin_password :
file : ./secrets/grafana_admin_password.txt
# PostgreSQL service
postgres :
secrets :
- postgres_password
environment :
- POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password
# Redis service
redis :
secrets :
- redis_password
command : >-
sh -c '
if [ -f /run/secrets/redis_password ]; then
REDIS_PASSWORD=$(cat /run/secrets/redis_password)
redis-server --requirepass "$REDIS_PASSWORD"
else
redis-server
fi
'
Verify Secrets # Check secrets are mounted
podman compose -f docker-compose.prod.yml exec postgres ls -la /run/secrets/
podman compose -f docker-compose.prod.yml exec redis ls -la /run/secrets/
Secret Rotation # 1. Update secret file
echo "new_password" > secrets/postgres_password.txt
chmod 600 secrets/postgres_password.txt
# 2. Restart affected service
podman compose -f docker-compose.prod.yml restart postgres
# 3. Restart dependent services
podman compose -f docker-compose.prod.yml restart backend
Secrets vs Environment Variables Feature Environment Variables Docker Secrets Setup complexity Simple Moderate Visible in docker inspect Yes No Visible in process listing Yes No Credential rotation Rebuild required Restart only Best for Development Production
Security Configuration Default Security Posture Feature Default Production Recommendation Authentication Single-user model: SetupGuardMiddleware returns 503 until the first admin registers, then API endpoints are open (verify_api_key guards the DLQ/inbound-webhook routes, not admin; /api/admin/* is guarded by require_admin_access on ADMIN_ENABLED alone) Keep network binding to 127.0.0.1 as the primary boundary; enable API keys for anything exposed HTTPS/TLS Disabled Enable Rate Limiting Enabled Keep enabled Admin Endpoints Enabled — ADMIN_ENABLED=true by default; DEBUG is not consulted, ADMIN_API_KEY enforces nothing Keep off non-loopback interfaces, or set ADMIN_ENABLED=false Debug Mode Disabled Keep disabled CORS Localhost origins (incl. https://localhost:8444) Restrict to your domains
API Key Authentication # Enable in .env
API_KEY_ENABLED = true
API_KEYS =[ "your-secure-api-key-here" , "another-key-if-needed" ]
Making authenticated requests:
curl -H "X-API-Key: your-secure-api-key-here" \
http://localhost:8000/api/events
Generate secure keys:
python -c "import secrets; print(secrets.token_urlsafe(32))"
# or
openssl rand -base64 32
Admin Endpoint Security Admin endpoints are gated by one condition:
ADMIN_ENABLED=true (the default — DEBUG is not consulted) # Admin endpoints are enabled by default; this is the only switch
ADMIN_ENABLED = true
# Reserved — NOT enforced: no code path reads ADMIN_API_KEY and no
# X-Admin-API-Key header is validated. Setting it changes nothing.
# ADMIN_API_KEY=your-admin-api-key
Admin exposure is therefore controlled by the network bind (127.0.0.1 / a trusted network) or by setting ADMIN_ENABLED=false.
CORS Configuration # Development
CORS_ORIGINS =[ "http://localhost:3000" , "http://localhost:5173" ]
# Production
CORS_ORIGINS =[ "https://your-domain.com" ]
Firewall Configuration Only expose necessary ports:
Port Service Exposure 8080 / 8444 Frontend (HTTP/HTTPS) User access 8000 Backend API User access (binds 127.0.0.1 by default) 5432 PostgreSQL Internal only 6379 Redis Internal only 8090, 8002 ai-gateway (API, Triton metrics) Internal only 8091 ai-llm (Nemotron) Internal only
# UFW example (Linux)
ufw allow 8080 /tcp
ufw allow 8444 /tcp
ufw allow 8000 /tcp
ufw deny 5432 /tcp
ufw deny 6379 /tcp
ufw enable
Database Configuration # PostgreSQL (required - SQLite not supported)
DATABASE_URL = postgresql+asyncpg://username:password@host:port/database # pragma: allowlist secret
# Examples:
# Docker Compose
DATABASE_URL = postgresql+asyncpg://security:your_password@postgres:5432/security # pragma: allowlist secret
# Native development
DATABASE_URL = postgresql+asyncpg://security:your_password@localhost:5432/security # pragma: allowlist secret
Required Setup SECURITY: Database password is REQUIRED. No default passwords exist.
# Generate secure password
openssl rand -base64 32
# Create .env
POSTGRES_USER = security
POSTGRES_PASSWORD = your-secure-generated-password
POSTGRES_DB = security
DATABASE_URL = postgresql+asyncpg://security:your-secure-generated-password@postgres:5432/security # pragma: allowlist secret
Schema Migrations There is no Alembic step — the Alembic migrations tree was removed in PR #4465. The schema is created/synced by init_db() (Base.metadata.create_all) on backend startup. See Migrations for the details.
Database Operations # Connect to database
podman compose -f docker-compose.prod.yml exec postgres psql -U security -d security
# Backup
podman compose -f docker-compose.prod.yml exec -T postgres pg_dump -U security -d security -F c > backup.dump
# Restore
podman compose -f docker-compose.prod.yml exec -T postgres pg_restore -U security -d security < backup.dump
Redis Configuration # Standard Redis
REDIS_URL = redis://localhost:6379/0
# Redis with password
REDIS_URL = redis://:password@localhost:6379/0
# Redis TLS
REDIS_URL = rediss://localhost:6379/0
Password Authentication # Generate password
openssl rand -base64 32
# Add to .env
REDIS_PASSWORD = your_generated_password
When REDIS_PASSWORD is set, both Redis container and backend automatically use it.
Redis Operations # Test connection
podman compose -f docker-compose.prod.yml exec redis redis-cli ping
# Get info
podman compose -f docker-compose.prod.yml exec redis redis-cli INFO memory
# Clear cache (WARNING: loses cached data)
podman compose -f docker-compose.prod.yml exec redis redis-cli FLUSHALL
AI Service Configuration Service URLs All vision models live in the single ai-gateway container, so there are two base URLs; the per-service variables carry the router path , not a per-model port (the old 8092-8096 ports no longer exist). See Deployment Modes .
# Production (docker-compose.prod.yml defaults)
USE_AI_GATEWAY = true
AI_GATEWAY_URL = http://ai-gateway:8090
NEMOTRON_URL = http://ai-llm:8091
YOLO26_URL = http://ai-gateway:8090/yolo26
FLORENCE_URL = http://ai-gateway:8090/florence
CLIP_URL = http://ai-gateway:8090/clip
ENRICHMENT_URL = http://ai-gateway:8090/enrichment
ENRICHMENT_LIGHT_URL = http://ai-gateway:8090/enrich-lt
# Development (AI on host; gateway/router listens on host port 8090)
AI_GATEWAY_URL = http://localhost:8090
NEMOTRON_URL = http://localhost:8091
YOLO26_URL = http://localhost:8090/yolo26
# Backend in container + AI on host (Docker Desktop macOS/Windows)
AI_GATEWAY_URL = http://host.docker.internal:8090
NEMOTRON_URL = http://host.docker.internal:8091
Feature Toggles Variable Default Description VISION_EXTRACTION_ENABLED true Enable Florence-2 extraction REID_ENABLED true Enable person re-identification (OSNet-AIN x1.0) SCENE_CHANGE_ENABLED true Enable scene change detection
TLS/HTTPS Configuration TLS Modes Mode Use Case Certificate Source disabled Development only None self_signed LAN/internal use Auto-generated provided Production Your certificates
Self-Signed Certificates # .env
TLS_MODE = self_signed
TLS_CERT_DIR = data/certs
# Certificates auto-generated on first start
Production Certificates # .env
TLS_MODE = provided
TLS_CERT_PATH = /etc/ssl/certs/server.crt
TLS_KEY_PATH = /etc/ssl/private/server.key
TLS_MIN_VERSION = TLSv1.2
Generate Self-Signed Certificates mkdir -p data/certs
cd data/certs
# Generate private key
openssl genrsa -out server.key 2048
# Generate certificate (365 days)
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=home-security-intelligence/O=Local Development"
# Set permissions
chmod 600 server.key
chmod 644 server.crt
TLS Variables Variable Default Description TLS_MODE disabled disabled, self_signed, provided TLS_CERT_PATH none Certificate file path TLS_KEY_PATH none Private key file path TLS_CA_PATH none CA certificate (for mTLS) TLS_VERIFY_CLIENT false Enable mTLS TLS_MIN_VERSION TLSv1.2 Minimum TLS version
Rate Limiting Rate limiting is enabled by default:
RATE_LIMIT_ENABLED = true
RATE_LIMIT_REQUESTS_PER_MINUTE = 60
RATE_LIMIT_BURST = 10
RATE_LIMIT_MEDIA_REQUESTS_PER_MINUTE = 120
RATE_LIMIT_WEBSOCKET_CONNECTIONS_PER_MINUTE = 100
RATE_LIMIT_SEARCH_REQUESTS_PER_MINUTE = 30
Rate Limit Tiers Endpoint Type Limit/min Purpose General API 60 Normal operations Media (images/thumbnails) 120 Higher for dashboards Search 30 Lower (expensive operation) WebSocket 100 connection attempts/min Prevent connection storms
Logging Configuration Variable Default Description LOG_LEVEL WARNING DEBUG, INFO, WARNING, ERROR, CRITICAL LOG_FILE_PATH data/logs/security.log Rotating log file LOG_FILE_MAX_BYTES 10485760 Max log file size (10MB) LOG_FILE_BACKUP_COUNT 7 Number of backup files LOG_DB_ENABLED true Write logs to database LOG_DB_MIN_LEVEL DEBUG Minimum level for DB
Data Retention Retention Settings Variable Default Range Description RETENTION_DAYS 30 1-365 Events/detections retention LOG_RETENTION_DAYS 7 1-365 Database logs retention
Automated Cleanup Cleanup runs daily at 03:00.
# Preview cleanup (dry run) — the endpoint is guarded by verify_api_key, so send
# X-API-Key (validated against API_KEYS) when API_KEY_ENABLED=true
curl -X POST "http://localhost:8000/api/system/cleanup?dry_run=true"
# Execute cleanup
curl -X POST "http://localhost:8000/api/system/cleanup"
Storage Estimates Deployment Cameras Recommended Disk Retention Small 1-4 50GB 30 days Medium 5-8 100GB 30 days Large 8+ 250GB+ 14-30 days
Security Checklist Development [ ] Debug mode can be enabled [ ] Self-signed certs acceptable [ ] CORS allows localhost Production [ ] DEBUG=false [ ] ADMIN_ENABLED=false [ ] TLS with valid certificates [ ] API keys required [ ] POSTGRES_PASSWORD set (required) [ ] Strong, unique passwords (32+ chars) [ ] .env file permissions are 600 [ ] Docker secrets for credentials [ ] Firewall configured [ ] Database not exposed externally [ ] Redis not exposed externally [ ] AI services not exposed externally [ ] CORS restricted to your domain [ ] Log retention configured [ ] Backups encrypted Example Configurations Development DATABASE_URL = postgresql+asyncpg://security:dev_password@localhost:5432/security # pragma: allowlist secret
REDIS_URL = redis://localhost:6379/0
YOLO26_URL = http://localhost:8090/yolo26
NEMOTRON_URL = http://localhost:8091
FOSCAM_BASE_PATH = /export/foscam
DEBUG = true
LOG_LEVEL = DEBUG
Production DATABASE_URL = postgresql+asyncpg://security:secure_password@postgres:5432/security # pragma: allowlist secret
REDIS_URL = redis://redis:6379
USE_AI_GATEWAY = true
AI_GATEWAY_URL = http://ai-gateway:8090
YOLO26_URL = http://ai-gateway:8090/yolo26
NEMOTRON_URL = http://ai-llm:8091
DEBUG = false
LOG_LEVEL = WARNING
RETENTION_DAYS = 30
DETECTION_CONFIDENCE_THRESHOLD = 0 .6
API_KEY_ENABLED = true
API_KEYS =[ "your-secure-api-key" ]
TLS_MODE = provided
TLS_CERT_PATH = /path/to/server.crt
TLS_KEY_PATH = /path/to/server.key
Validation # Check backend config loads correctly (run from the repo root; DATABASE_URL must be set)
uv run python -c "from backend.core.config import get_settings; s = get_settings(); print(s.model_dump_json(indent=2))"
# Test service connectivity
curl http://localhost:8000/api/system/health # Backend
curl http://localhost:8090/yolo26/health # ai-gateway /yolo26 router
curl http://localhost:8091/health # Nemotron
redis-cli ping # Redis
See Also