All keys are shown with their defaults. 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.
| Key | Env var | Default | Description |
|---|
env | ALTAVMS_ENV | development | Deployment environment: development | staging | production. Governs validation strictness |
| Key | Env var | Default | Description |
|---|
http.addr | ALTAVMS_HTTP_ADDR | :3303 | Listen address and port |
http.read_timeout | ALTAVMS_HTTP_READ_TIMEOUT | 15s | Max time to read a full request |
http.write_timeout | ALTAVMS_HTTP_WRITE_TIMEOUT | 30s | Max time to write a response |
http.idle_timeout | ALTAVMS_HTTP_IDLE_TIMEOUT | 60s | Keep-alive idle timeout |
http.shutdown_grace | ALTAVMS_HTTP_SHUTDOWN_GRACE | 10s | Grace period for in-flight requests on shutdown |
http.pre_drain_delay | ALTAVMS_HTTP_PREDRAIN_DELAY | 5s | Delay after /readyz starts failing before shutdown (for load balancers) |
http.tls_enabled | ALTAVMS_HTTP_TLS_ENABLED | false | Set true when fronted by TLS; enables HSTS in production |
http.secure_cookies | ALTAVMS_HTTP_SECURE_COOKIES | false | Mark session/auth cookies Secure (HTTPS-only). Enable in any TLS-fronted deployment |
http.allowed_origins | ALTAVMS_ALLOWED_ORIGINS | ["http://localhost:3303"] | CORS allow-list. Staging and production reject * and an empty list at startup |
http.internal_addr | ALTAVMS_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. Every response also carries X-Frame-Options: DENY; the UI cannot be embedded in an iframe, and there is no toggle.
| Key | Env var | Default | Description |
|---|
db.path | ALTAVMS_DB_PATH | ./data/altavms.db | SQLite file path (parent dir must exist) |
db.connect_timeout | ALTAVMS_DB_CONNECT_TIMEOUT | 5s | Connection open timeout |
db.query_timeout | ALTAVMS_DB_QUERY_TIMEOUT | 30s | Single-query timeout |
| Key | Env var | Default | Description |
|---|
log.level | ALTAVMS_LOG_LEVEL | info | debug | info | warn | error |
log.format | ALTAVMS_LOG_FORMAT | json | json (production) | text (development) |
| Key | Env var | Default | Description |
|---|
auth.jwt_secret | ALTAVMS_AUTH_JWT_SECRET | (empty) | JWT signing secret. Required in staging/production |
auth.access_ttl | ALTAVMS_AUTH_ACCESS_TTL | 15m | Lifetime of the short-lived access token (> 0, ≤ 1h) |
auth.refresh_ttl | ALTAVMS_AUTH_REFRESH_TTL | 168h | Lifetime of the rotating refresh session (> 0, ≤ 720h). An idle browser session expires after this |
auth.oauth2_secret | ALTAVMS_OAUTH2_SECRET | (empty) | Base64 32-byte secret to verify inbound OAuth2 client-credentials tokens on the recognition endpoint. When unset, POST /api/v1/events is not mounted |
crypto.encryption_keys | ALTAVMS_ENCRYPTION_KEY, _V2, _V3, … | [] | Base64 AES-256-GCM keys for encrypted DB fields. First key is active; older keys decrypt during rotation. Required in production |
Generate a key with openssl rand -base64 32. 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/altavms/altavms.env.
| Key | Env var | Default | Description |
|---|
bootstrap.admin_email | ADMIN_EMAIL | (empty) | Initial admin email. Dev fallback: admin@localhost |
bootstrap.admin_password | ADMIN_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.
| Key | Env var | Default | Description |
|---|
alta.default_timeout | ALTAVMS_ALTA_DEFAULT_TIMEOUT | 10s | Per-attempt timeout for CRUD calls |
alta.long_timeout | ALTAVMS_ALTA_LONG_TIMEOUT | 30s | Per-attempt timeout for Preview/VOD calls |
alta.breaker_threshold | ALTAVMS_ALTA_BREAKER_THRESHOLD | 5 | Consecutive failures before the circuit breaker opens |
alta.breaker_open_for | ALTAVMS_ALTA_BREAKER_OPEN_FOR | 30s | How long the breaker stays open |
alta.bulkhead_capacity | ALTAVMS_ALTA_BULKHEAD_CAPACITY | 64 | Max concurrent in-flight Alta calls |
alta.stream.hls_token_ttl | ALTAVMS_ALTA_STREAM_HLS_TOKEN_TTL | 5m | TTL for HLS playlist tokens (≤ 1h) |
alta.stream.webrtc_token_ttl | ALTAVMS_ALTA_STREAM_WEBRTC_TOKEN_TTL | 2m | TTL for WHEP bearer tokens (≤ 1h) |
alta.stream.archive_token_ttl | ALTAVMS_ALTA_STREAM_ARCHIVE_TOKEN_TTL | 5m | TTL for archive (VOD) playback tokens (≤ 1h) |
alta.stream.preview_list_max_window | ALTAVMS_ALTA_STREAM_PREVIEW_LIST_MAX_WINDOW | 2h | Max time span a single preview-list request may cover (≤ 24h) |
alta.stream.auth_mode | ALTAVMS_ALTA_STREAM_AUTH_MODE | jwt | jwt (production) | none (dev-only, logs a warning) |
| Key | Env var | Default | Description |
|---|
workers.enabled | ALTAVMS_WORKERS_ENABLED | all workers | Comma-separated allow-list from {motion, clips, screenshots, cleanup, health_server, health_camera, health, alta_prober, alerts, notifications, io, reconcile}. true = all (the default), false = none. health is a legacy alias that enables both health workers; webhooks is accepted for backward compatibility but does nothing; any other unknown name aborts startup |
workers.motion_interval | ALTAVMS_WORKERS_MOTION_INTERVAL | 1s | Motion reconcile interval per camera |
workers.motion_concurrency | ALTAVMS_WORKERS_MOTION_CONCURRENCY | 4 | Max cameras processed concurrently per motion tick (≥ 1) |
workers.health_probe_concurrency | ALTAVMS_WORKERS_HEALTH_PROBE_CONCURRENCY | 8 | Max concurrent Alta health probes per tick (≥ 1) |
workers.clips_output_path | CLIPS_OUTPUT_PATH | /var/lib/altavms/clips | Root for exported clip MP4s |
workers.clips_ffmpeg_binary | CLIPS_FFMPEG_BINARY | ffmpeg | Path to the ffmpeg executable |
workers.clips_worker_timeout | CLIPS_WORKER_TIMEOUT | 5m | Per-clip ffmpeg deadline |
workers.clips_worker_concurrency | CLIPS_WORKER_CONCURRENCY | 2 | Max concurrent ffmpeg subprocesses |
workers.screenshots_output_path | SCREENSHOTS_OUTPUT_PATH | /var/lib/altavms/screenshots | Root for screenshot JPEGs |
workers.notifications_dispatch_concurrency | ALTAVMS_NOTIFICATIONS_DISPATCH_CONCURRENCY | 8 | Max concurrent goroutines dispatching emails, push, and webhooks |
workers.drain_timeout | ALTAVMS_WORKERS_DRAIN_TIMEOUT | 10s | Total budget to drain workers on shutdown |
The io worker monitors camera IO triggers via ONVIF event subscriptions. 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.
| Key | Env var | Default | Description |
|---|
retention.audit_days | ALTAVMS_RETENTION_AUDIT_DAYS | 365 | First-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.
| Key | UI | Default | Range | Description |
|---|
connection.camera_health_interval | Camera health interval | 3 | 1–30 | Camera health poll interval, in seconds |
connection.server_ping_interval | Server ping interval | 1 | 1–10 | Alta availability probe interval, in seconds. Sets how fast outages and restarts are detected |
connection.server_health_interval | Server status | 15 | 10–300 | Host/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_interval | Server config | 300 | 60–3600 | Config-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) | 600 | 60–86400 | How long an Alta resource with no matching VMS 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) | 20 | 1–1000 | Per-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) | 1 | 0–100 | Refuses 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.
| Key | Env var | Default | Description |
|---|
maps_background_path | MAPS_BACKGROUND_PATH | /var/lib/altavms/maps | Filesystem root for floor-plan background images |
| Key | Env var | Default | Description |
|---|
metrics.enabled | ALTAVMS_METRICS_ENABLED | true | Expose Prometheus metrics at /metrics |
readiness.timeout | ALTAVMS_READINESS_TIMEOUT | 5s | Overall deadline for /readyz |
readiness.check_workers | ALTAVMS_READINESS_CHECK_WORKERS | true | Include worker liveness in readiness |
readiness.check_schema | ALTAVMS_READINESS_CHECK_SCHEMA | true | Include schema-version check |
readiness.worker_stale_multiplier | ALTAVMS_READINESS_WORKER_STALE_MULTIPLIER | 2.0 | A worker is stale when its last heartbeat is older than multiplier × tick |
| Key | Env var | Default | Description |
|---|
security.csp_strict_mode | ALTAVMS_SECURITY_CSP_STRICT | false | Opt in to strict CSP in dev (always strict in production) |
startup.check_schema | ALTAVMS_STARTUP_CHECK_SCHEMA | true | Verify DB schema on boot |
startup.check_environment | ALTAVMS_STARTUP_CHECK_ENVIRONMENT | true | Verify environment on boot |
startup.check_keyring | ALTAVMS_STARTUP_CHECK_KEYRING | true | Verify encryption keyring on boot |
startup.check_alta | ALTAVMS_STARTUP_CHECK_ALTA | true | Verify Alta reachability on boot. Set false to start before any Alta server is configured |
startup.timeout | ALTAVMS_STARTUP_TIMEOUT | 10s | Total budget for startup checks |
Per-user token bucket, keyed on user ID. Public endpoints (/healthz, /readyz, /metrics, /auth/verify) are exempt.
| Key | Env var | Default | Description |
|---|
rate_limit.enabled | ALTAVMS_RATE_LIMIT_ENABLED | true | Enable the limiter |
rate_limit.read_per_min | ALTAVMS_RATE_LIMIT_READ_PER_MIN | 60 | Read requests per minute |
rate_limit.write_per_min | ALTAVMS_RATE_LIMIT_WRITE_PER_MIN | 20 | Write requests per minute |
rate_limit.burst | ALTAVMS_RATE_LIMIT_BURST | 0 | Burst allowance (0 = use per-minute budget) |
Independent of the per-user limiter above, this protects the unauthenticated auth endpoints (login, refresh, password reset) with a per-key throttle and progressive brute-force lockout. Login is keyed by the submitted email; refresh/reset by client IP. Counters are in-process and reset on restart.
| Key | Env var | Default | Description |
|---|
auth_rate_limit.enabled | ALTAVMS_AUTH_RATE_LIMIT_ENABLED | true | Enable the auth throttle/lockout |
auth_rate_limit.per_min | ALTAVMS_AUTH_RATE_LIMIT_PER_MIN | 5 | Sustained attempts per minute per key |
auth_rate_limit.burst | ALTAVMS_AUTH_RATE_LIMIT_BURST | 5 | Bucket capacity (0 = use per_min) |
auth_rate_limit.max_failures | ALTAVMS_AUTH_RATE_LIMIT_MAX_FAILURES | 5 | Consecutive failures before lockout |
auth_rate_limit.lockout_base | ALTAVMS_AUTH_RATE_LIMIT_LOCKOUT_BASE | 1m | First lockout; doubles each further failure |
auth_rate_limit.lockout_max | ALTAVMS_AUTH_RATE_LIMIT_LOCKOUT_MAX | 15m | Cap on the progressive backoff |
auth_rate_limit.ttl | ALTAVMS_AUTH_RATE_LIMIT_TTL | 15m | Idle window before a key is evicted |
auth_rate_limit.max_keys | ALTAVMS_AUTH_RATE_LIMIT_MAX_KEYS | 4096 | Hard cap on tracked keys (bounds memory) |