# ============================================================================== # ___ _ _______ __ __ # / | (_) ____/ |/ / / /___ _____ ____ _____ ____ _____ # / /| |/ / / / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ # / ___ / / /___/ / / / /_/ / /_/ / /_/ / /_/ / __/ / # /_/ |_\_\____/_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ # /____/ # ============================================================================== # One manager to rule them all. Local-first, Encrypted, Powerful. # ============================================================================== # # This file is GENERATED from the validated schema in server/lib/env.js. # Every variable below is checked at server boot: wrong types or out-of-range # values stop the process with a message naming the variable. Defaults shown # here are the exact code defaults. # # Regenerate after changing the schema: npm --prefix server run gen:env # # ============================================================================== # ESSENTIAL SETUP # ============================================================================== # --- PORT --- # Port the server listens on (bound to 0.0.0.0). Local development tip: use # 16100 to avoid clashing with a Docker container mapped to host port 1610. # integer | default: 1610 | 1..65535 PORT=1610 # --- NODE_ENV --- # Runtime mode. Only "development" changes behavior (verbose/dev helpers in a # few modules); all other values including "production", "staging", "prod", etc. # are treated identically. # string | default: production NODE_ENV=production # --- DATA_DIR --- # Directory for the SQLite database, the generated server_secret.key and traces. # In Docker this must match the volume mount path (e.g. /app/data). Default: # /data # string | default: /data (Docker: /app/data) #DATA_DIR=/app/data # --- MAX_SYNC_PAYLOAD_SIZE --- # Maximum size in bytes of the encrypted sync-state blob accepted by the sync # API. Default: 100 MB (104857600). # integer | default: 104857600 | >= 1 #MAX_SYNC_PAYLOAD_SIZE=104857600 # ============================================================================== # DATABASE # ============================================================================== # Pool settings are ignored when DB_TYPE=sqlite. # --- DB_TYPE --- # Database engine: sqlite (zero-config, file-based) or postgres (recommended for # scale / multi-tenant). # string | one of: sqlite, postgres DB_TYPE=sqlite # --- DATABASE_URL --- # PostgreSQL connection string, e.g. postgres://user:password@host:5432/dbname. # REQUIRED when DB_TYPE=postgres, ignored for sqlite. Leave unset for SQLite. # string | optional #DATABASE_URL= # --- DB_SSL_REJECT_UNAUTHORIZED --- # Verify the PostgreSQL server TLS certificate. Disabled by default because many # providers use self-signed certificates; enable only with a trusted, pinned # certificate path. # boolean | default: false #DB_SSL_REJECT_UNAUTHORIZED=false # --- DB_POOL_SIZE --- # Maximum number of PostgreSQL pool connections (ignored when DB_TYPE=sqlite). # integer | default: 20 | >= 1 #DB_POOL_SIZE=20 # --- DB_CONNECTION_TIMEOUT --- # Milliseconds to wait for a pool connection before timing out (PostgreSQL # only). # integer | default: 10000 | >= 1 #DB_CONNECTION_TIMEOUT=10000 # --- DB_MAX_RETRIES --- # Number of PostgreSQL connection attempts on startup with exponential backoff. # integer | default: 5 | >= 1 #DB_MAX_RETRIES=5 # --- READ_ONLY_REPLICA --- # Run as a read-only standby against a replicated PostgreSQL database: skips # schema/migrations and never starts the autopilot or activity workers, so the # standby makes no background writes against your accounts. GET /api/health # stays 200 while GET /api/ready returns 503 — point write-routing probes at # /api/ready. Replicated rows are encrypted with the primary key: set # ENCRYPTION_KEY (or copy server_secret.key) to the SAME value as the primary, # otherwise the standby serves empty configs/addons. To take over: promote the # replica (pg_promote) and restart with this unset. # boolean | default: false #READ_ONLY_REPLICA=false # ============================================================================== # SECURITY # ============================================================================== # --- ENCRYPTION_KEY --- # Encrypts sensitive data at rest (autopilot rules, Stremio auth keys, # connection credentials). Zero-config: when empty, a secure random key is # generated on first boot and saved to DATA_DIR/server_secret.key. Setting a # value later keeps the old file key as a decrypt fallback so existing data # stays readable. # string | optional #ENCRYPTION_KEY= # --- CORS_ORIGINS --- # Comma-separated allow-list of browser origins for the API, e.g. # https://app.example.com,https://stremio.example.com. Empty = localhost-only # development defaults. # comma-separated list | default: localhost development origins (3000/5173/4173) #CORS_ORIGINS= # --- SSRF_ALLOW_PRIVATE --- # Allow the metadata proxy to fetch addon manifests from private/internal IP # ranges (Docker networks, DNS rewrites, reverse proxies). Only enable on # trusted self-hosted instances behind a firewall. # boolean | default: false #SSRF_ALLOW_PRIVATE=false # --- TRUST_PROXY --- # Trust X-Forwarded-For headers from the reverse proxy (Traefik/nginx/NPM). # Required behind a proxy for per-client rate limiting to see real client IPs; # without it all proxied clients share one rate-limit bucket. # WARNING: your proxy must OVERWRITE X-Forwarded-For with the real client IP # ($remote_addr), not append to it. With append-style proxies this setting lets # clients pick their own IP by sending a forged X-Forwarded-For header, which # defeats rate limiting and forges logs. # boolean | default: false #TRUST_PROXY=false # --- CUSTOM_HTML --- # Raw HTML injected at the top of the login/configuration page (hosted-instance # banners, announcements). # string | optional #CUSTOM_HTML= # --- REGISTRATIONS_CLOSED --- # Block new account creation while existing users can still sign in. # boolean | default: false #REGISTRATIONS_CLOSED=false # --- UNIFIED_ENFORCEMENT --- # Unify certain server-side enforcement checks across modules. Leave disabled # for the default per-module behavior. # boolean | default: false #UNIFIED_ENFORCEMENT=false # ============================================================================== # LOGGING # ============================================================================== # --- LOG_LEVEL --- # Server log verbosity: fatal, error, warn, info, debug or trace. # string | one of: fatal, error, warn, info, debug, trace LOG_LEVEL=info # --- LOG_PRETTY_PRINT --- # Pretty-print logs for humans (true) or emit JSON lines for log shippers # (false). Recommended: false in production. # boolean | default: true LOG_PRETTY_PRINT=true # ============================================================================== # PROXY & IMAGE CACHING # ============================================================================== # --- PROXY_CONCURRENCY_LIMIT --- # Maximum concurrent outbound proxy/metadata requests the server fans out. # integer | default: 50 | >= 1 #PROXY_CONCURRENCY_LIMIT=50 # --- AIOSTREAMS_USER_API_THROTTLE_MS --- # Minimum interval in ms between calls to a single AIOStreams user API endpoint. # integer | default: 1000 | >= 200 #AIOSTREAMS_USER_API_THROTTLE_MS=1000 # --- IMAGE_PROXY_TIMEOUT_MS --- # Per-request timeout in ms for the poster/image proxy. Lower fails fast so the # shared queue drains instead of clogging on a slow image source. # integer | default: 4000 | >= 1000 #IMAGE_PROXY_TIMEOUT_MS=4000 # --- IMAGE_PROXY_QUEUE_LIMIT --- # Max concurrent in-flight image-proxy requests; isolates poster storms from the # stream/manifest proxies sharing the global queue. # integer | default: 150 | >= 30 #IMAGE_PROXY_QUEUE_LIMIT=150 # --- IMAGE_PROXY_THROTTLE_MS --- # Minimum interval in ms between image-proxy calls to a single host. MetaHub is # a CDN, so this stays far below the global throttle. 0 disables. # integer | default: 25 | >= 0 #IMAGE_PROXY_THROTTLE_MS=25 # --- IMAGE_CACHE_TTL_MS --- # TTL in ms for the shared in-memory poster cache: hot posters serve from memory # and identical cold requests collapse into one upstream fetch. 0 disables. # integer | default: 86400000 | >= 0 #IMAGE_CACHE_TTL_MS=86400000 # --- IMAGE_CACHE_MAX_BYTES --- # Total byte budget for the in-memory poster cache (bounded by bytes, not entry # count). Default: 128 MB. 0 disables the cache. # integer | default: 134217728 | >= 0 #IMAGE_CACHE_MAX_BYTES=134217728 # --- IMAGE_CACHE_MAX_ITEM_BYTES --- # Reject single cache entries larger than this many bytes. Default: 512 KB. 0 # disables the limit. # integer | default: 524288 | >= 0 #IMAGE_CACHE_MAX_ITEM_BYTES=524288 # ============================================================================== # ACTIVITY ENGINE (OPT-IN) # ============================================================================== # Safe defaults shown — only relevant when you explicitly opt into server-side # activity capture via ACTIVITY_ENGINE_ENABLED. Events are kept permanently so # Activity can behave like Replay. # --- ACTIVITY_ENGINE_ENABLED --- # Opt in to server-side background polling that captures watch activity (events # + snapshots) on a timer, so Activity/Replay history builds even with no client # open. Multi-tenant warning: every account is polled each cycle (the # write-amplification pattern behind the Midnight incident) — enable only on # single-user/trusted instances. Client-side Activity tracking works without # this. # boolean | default: false ACTIVITY_ENGINE_ENABLED=false # --- ACTIVITY_SCAN_INTERVAL_MS --- # Base delay in ms between activity scans. # integer | default: 300000 | >= 60000 ACTIVITY_SCAN_INTERVAL_MS=300000 # --- ACTIVITY_SCAN_JITTER_MS --- # Random extra delay in ms (0..value) spread over accounts to avoid a thundering # herd. # integer | default: 120000 | >= 0 ACTIVITY_SCAN_JITTER_MS=120000 # --- ACTIVITY_INITIAL_DELAY_MS --- # Delay in ms before the first scan after boot. # integer | default: 30000 | >= 5000 ACTIVITY_INITIAL_DELAY_MS=30000 # --- ACTIVITY_CYCLE_BUDGET_MS --- # Wall-clock budget in ms per scan cycle. # integer | default: 120000 | >= 10000 ACTIVITY_CYCLE_BUDGET_MS=120000 # --- ACTIVITY_MAX_ACCOUNTS_PER_CYCLE --- # Hard cap on accounts scanned per cycle. # integer | default: 50 | >= 1 ACTIVITY_MAX_ACCOUNTS_PER_CYCLE=50 # --- ACTIVITY_MAX_EVENTS_PER_CYCLE --- # Hard cap on activity events written per cycle. # integer | default: 1000 | >= 1 ACTIVITY_MAX_EVENTS_PER_CYCLE=1000 # --- ACTIVITY_MAX_SNAPSHOT_WRITES_PER_CYCLE --- # Hard cap on snapshot rows written per cycle. # integer | default: 5000 | >= 1 ACTIVITY_MAX_SNAPSHOT_WRITES_PER_CYCLE=5000 # --- ACTIVITY_BATCH_SIZE --- # Events accumulated before a batched database write. # integer | default: 40 | >= 1 ACTIVITY_BATCH_SIZE=40 # --- ACTIVITY_FETCH_TIMEOUT_MS --- # Per-account fetch timeout in ms. # integer | default: 10000 | >= 1000 ACTIVITY_FETCH_TIMEOUT_MS=10000 # --- ACTIVITY_HASH_CACHE_TTL_MS --- # TTL in ms for the per-account state-hash cache. # integer | default: 86400000 | >= 60000 ACTIVITY_HASH_CACHE_TTL_MS=86400000 # --- ACTIVITY_HASH_CACHE_MAX --- # Maximum number of accounts tracked in the state-hash cache. # integer | default: 5000 | >= 100 ACTIVITY_HASH_CACHE_MAX=5000 # ============================================================================== # AUTOPILOT ENGINE # ============================================================================== # --- AUTOPILOT_SCAN_CHUNK_SIZE --- # Maximum rules scanned per autopilot cycle. # integer | default: 500 | >= 100 #AUTOPILOT_SCAN_CHUNK_SIZE=500 # --- AUTOPILOT_MAX_RULES_PER_CYCLE --- # Hard cap on rules processed in a single cycle. Autopilot only writes tiny # timestamp updates, so large values are safe. Effectively max(scan chunk size, # this value). # integer | default: 10000 | >= 100 #AUTOPILOT_MAX_RULES_PER_CYCLE=10000 # --- AUTOPILOT_CYCLE_BUDGET_MS --- # Wall-clock budget in ms per autopilot cycle. With 1000+ rules, 120s gives # ample time for health checks. # integer | default: 120000 | >= 5000 #AUTOPILOT_CYCLE_BUDGET_MS=120000 # --- AUTOPILOT_HEALTH_CACHE_TTL_MS --- # How long addon health-check results are cached, in ms. # integer | default: 30000 | >= 10000 #AUTOPILOT_HEALTH_CACHE_TTL_MS=30000 # --- AUTOPILOT_RULE_RECHECK_MS --- # Minimum interval in ms before a rule is re-evaluated. # integer | default: 30000 | >= 10000 #AUTOPILOT_RULE_RECHECK_MS=30000 # --- AUTOPILOT_RULE_CACHE_MAX_BYTES --- # Byte budget for caching rules stable columns (addon_list/priority_chain) in # memory so the worker stops re-reading them every cycle — a big win on remote # databases (e.g. Supabase). Default: 256 MB. 0 disables. # integer | default: 268435456 | >= 0 #AUTOPILOT_RULE_CACHE_MAX_BYTES=268435456 # --- AUTOPILOT_RULE_CACHE_TTL_MS --- # Safety-net TTL in ms for the rule cache. Invalidation is event-driven (writes # bump updated_at); this only caps how long a stale entry could survive. # Default: 10 minutes. # integer | default: 600000 | >= 60000 #AUTOPILOT_RULE_CACHE_TTL_MS=600000 # ============================================================================== # INTERNAL / DEBUG # ============================================================================== # Do not set these manually. # --- SQLITE_DB_PATH --- # INTERNAL: set programmatically by database/setup.js at boot (defaults to # DATA_DIR/aio.db). Do not set manually. # string | optional #SQLITE_DB_PATH= # --- AIOMAN_TRACE --- # INTERNAL: request tracing to DATA_DIR/trace.log for debugging. Accepts # 1/true/yes/on. # boolean | default: false #AIOMAN_TRACE=false