Skip to content

Configuration Reference

All keys are shown with their defaults. Unless marked environment-only, each option can be set in config.yaml or via the listed environment variable, which takes precedence. Invalid values (non-positive timeouts, zero concurrency, out-of-range TTLs) abort startup with a config: validate: error naming the offending key.

KeyEnv varDefaultDescription
envVISION_ENVdevelopmentDeployment environment: development | staging | production. Governs validation strictness
KeyEnv varDefaultDescription
http.addrVISION_HTTP_ADDR:3303Listen address and port
http.read_timeoutVISION_HTTP_READ_TIMEOUT15sMax time to read a full request
http.write_timeoutVISION_HTTP_WRITE_TIMEOUT30sMax time to write a response
http.idle_timeoutVISION_HTTP_IDLE_TIMEOUT60sKeep-alive idle timeout
http.shutdown_graceVISION_HTTP_SHUTDOWN_GRACE10sGrace period for in-flight requests on shutdown
http.pre_drain_delayVISION_HTTP_PREDRAIN_DELAY5sDelay after /readyz starts failing before shutdown (for load balancers)
http.secure_cookiesVISION_HTTP_SECURE_COOKIEStrueMark session/auth cookies Secure (HTTPS-only). vision install writes false (plain http://); set true once HTTPS is in front
http.allowed_originsVISION_ALLOWED_ORIGINS["http://localhost:3303"]CORS allow-list. Staging and production reject * and an empty list at startup
http.internal_addrVISION_HTTP_INTERNAL_ADDR(empty)Separate listener for /healthz, /readyz, /metrics

Origins are matched as scheme://host[:port], case-insensitively, against the request’s Origin header. Cross-origin requests may use only the headers Authorization, Content-Type, X-Request-ID, Idempotency-Key, and Last-Event-ID — the header allow-list is fixed, and anything else fails preflight. The effective origin list is logged at startup as CORS allow-list. Production enables HSTS automatically. Every response also carries X-Frame-Options: DENY; the UI cannot be embedded in an iframe, and there is no toggle.

KeyEnv varDefaultDescription
db.pathVISION_DB_PATH./data/vision.dbSQLite file path (parent dir must exist)
db.connect_timeoutVISION_DB_CONNECT_TIMEOUT5sConnection open timeout
db.query_timeoutVISION_DB_QUERY_TIMEOUT30sSingle-query timeout
KeyEnv varDefaultDescription
log.levelVISION_LOG_LEVELinfodebug | info | warn | error
log.formatVISION_LOG_FORMATjsonjson (production) | text (development)
KeyEnv varDefaultDescription
auth.jwt_secretVISION_AUTH_JWT_SECRET(empty)JWT signing secret. Required in staging/production
auth.access_ttlVISION_AUTH_ACCESS_TTL15mLifetime of the short-lived access token (> 0, ≤ 1h)
auth.refresh_ttlVISION_AUTH_REFRESH_TTL168hLifetime of the rotating refresh session (> 0, ≤ 720h). An idle browser session expires after this
auth.oauth2_secretVISION_OAUTH2_SECRET(empty)Base64 32-byte secret for inbound OAuth2 client-credentials tokens. When unset, POST /api/v1/events is not mounted
crypto.encryption_keysVISION_ENCRYPTION_KEY, _V2, _V3, …[]Environment-only. Base64 AES-256-GCM keys for encrypted DB fields. First key is active; older keys decrypt during rotation. Required in staging/production

Generate a key with openssl rand -base64 32. A YAML value for crypto.encryption_keys is ignored; keep every key in /etc/vision/vision.env. Number rotated keys without gaps (_V2, _V3, …), because loading stops at the first missing number. The JWT secret accepts standard or URL-safe base64, padded or unpadded, and must decode to exactly 32 bytes.

These values seed the first admin user only while the users table is empty and are ignored afterwards — editing them once an admin exists has no effect. In development, missing values fall back to admin@localhost / admin (with a startup warning). In staging and production, the seed aborts startup when the database has no users and either value is missing. The installer generates both into /etc/vision/vision.env.

KeyEnv varDefaultDescription
bootstrap.admin_emailADMIN_EMAIL(empty)Initial admin email. Dev fallback: admin@localhost
bootstrap.admin_passwordADMIN_PASSWORD(empty)Initial admin password. Dev fallback: admin

Alta server connection details (base URL, credentials, and the per-server callback base URL) live in the database and are managed in the UI. These keys tune the shared client only.

KeyEnv varDefaultDescription
alta.default_timeoutVISION_ALTA_DEFAULT_TIMEOUT10sPer-attempt timeout for CRUD calls
alta.long_timeoutVISION_ALTA_LONG_TIMEOUT30sPer-attempt timeout for Preview/VOD calls
alta.breaker_thresholdVISION_ALTA_BREAKER_THRESHOLD5Consecutive failures before the circuit breaker opens
alta.breaker_open_forVISION_ALTA_BREAKER_OPEN_FOR30sHow long the breaker stays open
alta.bulkhead_capacityVISION_ALTA_BULKHEAD_CAPACITY64Max concurrent in-flight Alta calls
alta.stream.hls_token_ttlVISION_ALTA_STREAM_HLS_TOKEN_TTL5mTTL for HLS playlist tokens (≤ 1h)
alta.stream.webrtc_token_ttlVISION_ALTA_STREAM_WEBRTC_TOKEN_TTL2mTTL for WHEP bearer tokens (≤ 1h)
alta.stream.archive_token_ttlVISION_ALTA_STREAM_ARCHIVE_TOKEN_TTL5mTTL for archive (VOD) playback tokens (≤ 1h)
alta.stream.preview_list_max_windowVISION_ALTA_STREAM_PREVIEW_LIST_MAX_WINDOW2hMax time span a single preview-list request may cover (≤ 24h)
alta.stream.auth_modeVISION_ALTA_STREAM_AUTH_MODEjwtjwt (production) | none (dev-only, logs a warning)
KeyEnv varDefaultDescription
workers.enabledVISION_WORKERS_ENABLEDstandard workersComma-separated allow-list from {clips, cleanup, health_server, health_camera, health, alta_prober, alerts, notifications, io, reconcile}. true enables the standard set (excluding withdrawn io); false enables none. motion and screenshots are invalid and abort startup
workers.health_probe_concurrencyVISION_WORKERS_HEALTH_PROBE_CONCURRENCY8Max concurrent Alta health probes per tick (≥ 1)
workers.clips_output_pathCLIPS_OUTPUT_PATH/var/lib/vision/clipsLegacy path retained for existing records; new exports require an output storage
workers.clips_ffmpeg_binaryCLIPS_FFMPEG_BINARYffmpegPath to the ffmpeg executable
workers.clips_ffprobe_binaryCLIPS_FFPROBE_BINARYffprobePath to ffprobe, used when adding RTSP cameras and measuring exported clips
workers.clips_worker_timeoutCLIPS_WORKER_TIMEOUT5mPer-clip ffmpeg deadline
workers.clips_worker_concurrencyCLIPS_WORKER_CONCURRENCY2Max concurrent ffmpeg subprocesses
workers.screenshots_output_pathSCREENSHOTS_OUTPUT_PATH/var/lib/vision/screenshotsLegacy path retained for existing records; new captures require an output storage
workers.notifications_dispatch_concurrencyVISION_NOTIFICATIONS_DISPATCH_CONCURRENCY8Max concurrent goroutines dispatching emails, push, and webhooks
workers.drain_timeoutVISION_WORKERS_DRAIN_TIMEOUT10sTotal budget to drain workers on shutdown

The io worker remains available only for compatibility and logs a warning when explicitly enabled; its UI has been withdrawn. The alta_prober worker pings each Alta server about once a second to detect outages and restarts quickly; its cadence is the Server ping interval system setting below.

The reconcile worker runs hourly and deletes orphaned Alta-side resources — but only ones this installation can prove it created (a durable ownership record, not a name-pattern guess), so one installation can never delete another’s resources on a shared Alta server. See Connect an Alta Server for what this means day to day, and the Reconcile rows below for the settings that bound how much it deletes per pass.

The cleanup worker enforces retention. The audit-log policy below is seeded on first install; once written, the runtime source of truth is the in-app setting under Settings → Storages.

KeyEnv varDefaultDescription
retention.audit_daysVISION_RETENTION_AUDIT_DAYS365First-install seed for audit-log retention, in days

Health polling and reconcile tuning live in the database, not config.yaml. Every key below is writable via PUT /api/v1/settings/{key}; the UI column shows which ones also have a control on Settings → Connections today — the rest are settings-API/CLI-only for now.

KeyUIDefaultRangeDescription
connection.camera_health_intervalCamera health interval31–30Camera health poll interval, in seconds
connection.server_ping_intervalServer ping interval11–10Alta availability probe interval, in seconds. Sets how fast outages and restarts are detected
connection.server_health_intervalServer status1510–300Host/storage telemetry poll, plus the reachability corroboration and clean-poll debounce that clears a flapping server. Does not set outage-detection speed — that’s the ping interval above
connection.server_config_intervalServer config30060–3600Config-drift inspection cadence — how often the full Alta configuration is fetched to detect manual edits. The heaviest Alta call; keeping it infrequent is safe and doesn’t affect outage detection
connection.reconcile_grace_seconds(none)60060–86400How long an Alta resource with no matching Vision record must stay unreferenced and unclaimed before the reconcile worker deletes it. This isn’t a sweep kill switch — the same key throttles the health worker’s crash-recovery path; drop reconcile from workers.enabled to disable the sweep instead
connection.reconcile_max_deletes_per_tick(none)201–1000Per-server, per-tick cap on how many resources the reconcile sweep deletes; the remainder waits for the next tick
connection.reconcile_candidate_ratio_guard(none)10–100Refuses a server’s whole reconcile pass when delete candidates exceed references × this ratio — a brake against a bad reference read wiping a server. 0 disables the guard, for an intentional bulk cleanup

connection.server_health_interval and connection.server_config_interval were split from one setting (ADR-0045): reachability/status/telemetry polling and the heavier config-drift scan now run on independent cadences, so drift inspection can stay slow without also slowing outage detection. The two reconcile rails were added alongside the ownership-claim ledger — see Connect an Alta Server for what the sweep does with them.

KeyEnv varDefaultDescription
maps_background_pathMAPS_BACKGROUND_PATH(empty)Filesystem root for floor-plan images. Empty uses a maps directory beside db.path; the installer writes /var/lib/vision/maps
KeyEnv varDefaultDescription
metrics.enabledVISION_METRICS_ENABLEDtrueExpose Prometheus metrics at /metrics
readiness.timeoutVISION_READINESS_TIMEOUT5sOverall deadline for /readyz
readiness.check_workersVISION_READINESS_CHECK_WORKERStrueInclude worker liveness in readiness
readiness.check_schemaVISION_READINESS_CHECK_SCHEMAtrueInclude schema-version check
readiness.worker_stale_multiplierVISION_READINESS_WORKER_STALE_MULTIPLIER2.0A worker is stale when its last heartbeat is older than multiplier × tick
KeyEnv varDefaultDescription
security.csp_strict_modeVISION_SECURITY_CSP_STRICTfalseOpt in to strict CSP in dev (always strict in production)
startup.check_schemaVISION_STARTUP_CHECK_SCHEMAtrueVerify DB schema on boot
startup.check_environmentVISION_STARTUP_CHECK_ENVIRONMENTtrueVerify environment on boot
startup.check_keyringVISION_STARTUP_CHECK_KEYRINGtrueVerify encryption keyring on boot
startup.check_altaVISION_STARTUP_CHECK_ALTAtrueVerify Alta reachability on boot. Passes when no server is added yet
startup.timeoutVISION_STARTUP_TIMEOUT10sTotal budget for startup checks

Per-user token bucket, keyed on user ID. Public endpoints (/healthz, /readyz, /metrics, /auth/verify) are exempt.

Live video (starting and stopping streams) and PTZ use a separate real-time budget of 300 requests per minute, so paging a wall never eats the edit budget. A 429 says how long to wait.

KeyEnv varDefaultDescription
rate_limit.enabledVISION_RATE_LIMIT_ENABLEDtrueEnable the limiter
rate_limit.read_per_minVISION_RATE_LIMIT_READ_PER_MIN60Read requests per minute
rate_limit.write_per_minVISION_RATE_LIMIT_WRITE_PER_MIN60Write requests per minute
rate_limit.burstVISION_RATE_LIMIT_BURST0Burst allowance (0 = use per-minute budget)

Independent of the per-user limiter above, this protects unauthenticated login, refresh, and password-reset requests. Login must pass both the configured per-email budget and a fixed 60/minute source-IP budget. Email lockout is a flat 60 seconds. Session refresh is keyed by source IP and only rate-limited, never locked out — a burst of page reloads waits a few seconds instead of signing out. Password reset is keyed by source IP and uses the progressive lockout settings below. Counters are in-process and reset on restart.

KeyEnv varDefaultDescription
auth_rate_limit.enabledVISION_AUTH_RATE_LIMIT_ENABLEDtrueEnable the auth throttle/lockout
auth_rate_limit.per_minVISION_AUTH_RATE_LIMIT_PER_MIN5Sustained attempts per minute per key
auth_rate_limit.burstVISION_AUTH_RATE_LIMIT_BURST5Bucket capacity (0 = use per_min)
auth_rate_limit.max_failuresVISION_AUTH_RATE_LIMIT_MAX_FAILURES5Consecutive failures before lockout
auth_rate_limit.lockout_baseVISION_AUTH_RATE_LIMIT_LOCKOUT_BASE1mFirst lockout; doubles each further failure
auth_rate_limit.lockout_maxVISION_AUTH_RATE_LIMIT_LOCKOUT_MAX15mCap on the progressive backoff
auth_rate_limit.ttlVISION_AUTH_RATE_LIMIT_TTL15mIdle window before a key is evicted
auth_rate_limit.max_keysVISION_AUTH_RATE_LIMIT_MAX_KEYS4096Hard cap on tracked keys (bounds memory)