# ImageApi configuration template. Copy to `.env` and fill in for your # deploy. Comments mirror the canonical docs in CLAUDE.md — see there # for the full picture (especially the AI-Insights / Apollo / face # integration sections). # ── Required ──────────────────────────────────────────────────────────── DATABASE_URL=./database.db BASE_PATH=/path/to/media THUMBNAILS=/path/to/thumbnails VIDEO_PATH=/path/to/video/hls GIFS_DIRECTORY=/path/to/gifs PREVIEW_CLIPS_DIRECTORY=/path/to/preview-clips BIND_URL=0.0.0.0:8080 # Secondary loopback listener (default localhost:8088). Empty = don't bind it # (e.g. a second instance next to one that already owns 8088). # LOCAL_BIND_URL=localhost:8088 CORS_ALLOWED_ORIGINS=http://localhost:3000 SECRET_KEY=replace-me-with-a-long-random-secret RUST_LOG=info # ── File watching ─────────────────────────────────────────────────────── # Quick scan = recently-modified-files only; full scan = comprehensive walk. WATCH_QUICK_INTERVAL_SECONDS=60 WATCH_FULL_INTERVAL_SECONDS=3600 # Comma-separated path prefixes / component names to skip in /memories # AND in face detection (e.g. @eaDir, .thumbnails, /private). EXCLUDED_DIRS= # ── Video / HLS ───────────────────────────────────────────────────────── HLS_CONCURRENCY=2 HLS_TIMEOUT_SECONDS=900 # Sources above this bitrate (bits/s) or 1080p are transcoded instead of # stream-copied, so HLS segments stay small enough to start and seek quickly. HLS_COPY_MAX_BITRATE=20000000 PLAYLIST_CLEANUP_INTERVAL_SECONDS=86400 # ── Telemetry ──────────────────────────────────────────────────────────── # Span + log export over OTLP in any build when set (unset = nothing # exported). Logs carry trace ids, so SigNoz links them to traces. # RUST_LOG also gates export: keep it at info or debug. # OTLP_OTLS_ENDPOINT=http://localhost:4317 # ── AI Insights — LLMs ────────────────────────────────────────────────── # Models, backends, the embedding contract, TTS and agentic caps live in # llm.toml — copy llm.example.toml. Only the secret stays here. # LLM_CONFIG_PATH=./llm.toml # Enables the hybrid backend (chat via OpenRouter; photos stay local). # OPENROUTER_API_KEY=sk-or-... # # The old OLLAMA_* / LLAMA_SWAP_* / OPENROUTER_* / LLM_BACKEND / EMBED* / # AGENTIC_* / UNIFIED_SEARCH_MODEL vars still override llm.toml for now, with a # startup deprecation warning. Move them into llm.toml. # ── AI Insights — sibling services (optional) ─────────────────────────── # Apollo (places, face inference, CLIP encoders). Single-Apollo deploys # typically set only APOLLO_API_BASE_URL and let the face + CLIP # clients fall back to it. # APOLLO_API_BASE_URL=http://apollo.lan:8000 # APOLLO_FACE_API_BASE_URL=http://apollo.lan:8000 # APOLLO_CLIP_API_BASE_URL=http://apollo.lan:8000 # SMS_API_URL=http://localhost:8000 # SMS_API_TOKEN= # Display name used in agentic prompts when the LLM refers to "you". USER_NAME= # ── Face detection (Phase 3+) ─────────────────────────────────────────── # Cosine-sim floor for auto-binding a detected face to an existing # same-named person on detection. 0.4 ≈ moderate-confidence match. FACE_AUTOBIND_MIN_COS=0.4 # Per-scan-tick fan-out into Apollo's detect endpoint. Apollo's GPU # pool serializes server-side; this just overlaps file-IO with # inference RTT. FACE_DETECT_CONCURRENCY=8 # Per-detect HTTP timeout. CPU-only Apollo deploys may need higher. FACE_DETECT_TIMEOUT_SEC=60 # Per-tick caps on the two backlog drains (independent of WATCH_* # quick / full scans). Tune up if you have a large unscanned backlog # and want it to clear faster; tune down if Apollo is overloaded. FACE_BACKLOG_MAX_PER_TICK=64 FACE_HASH_BACKFILL_MAX_PER_TICK=2000 # ── CLIP semantic photo search ────────────────────────────────────────── # ImageApi calls Apollo's /api/internal/clip/{encode_image,encode_text} # to populate per-photo embeddings during the watcher's backlog drain # and to encode user queries at /photos/search time. Disabled when # neither APOLLO_CLIP_API_BASE_URL nor APOLLO_API_BASE_URL is set. # # Per-watcher-tick cap on the encode drain. Default 32 ≈ ~1 photo/sec # on CPU, ~30 photos/sec on a single-GPU host (Apollo's threadpool # is 1 on CUDA, so concurrency is bounded server-side regardless of # our setting). Bump on a fresh deploy to clear the backlog faster. CLIP_BACKLOG_MAX_PER_TICK=32 # Client-side parallel encode calls per drain pass. Apollo's GPU pool # serializes server-side; this just overlaps file-IO with inference. CLIP_ENCODE_CONCURRENCY=4 # Per-encode HTTP timeout. CPU-only Apollo deploys may need higher. CLIP_REQUEST_TIMEOUT_SEC=60 # ── RAG / search ──────────────────────────────────────────────────────── # Set to `1` to enable cross-encoder reranking on /search results. SEARCH_RAG_RERANK=0 # ── Nightly reel pre-generation (Phase 3+) ────────────────────────────── # Set to `1` to enable the scheduler. Disabled by default. # REEL_PREGEN_ENABLED=1 # Hour (0-23) when the nightly batch fires. Default 3 AM. # REEL_PREGEN_HOUR=3 # Day of week for weekly reels (0=Sun, 1=Mon, …). Default Monday. # REEL_PREGEN_WEEK_DOW=1 # Timezone offset in minutes from UTC (e.g., -480 = PST). Defaults to # the server's local timezone. # REEL_PREGEN_TZ_OFFSET_MINUTES= # Fixed timezone offset — overrides auto-detect to avoid DST shifts. # When set, both the DB fallback and env fallback use this value. # REEL_PREGEN_TZ_FIXED_MINUTES=-480 # Voice ID for narration (e.g., "grandma"). Falls back to the value # stored in the user_ai_prefs DB row when set. # REEL_PREGEN_VOICE= # Library filter: a library id (e.g. "1") or "all" for every library. # REEL_PREGEN_LIBRARY=all # Max agentic tool iterations for pre-gen scripter. Default 8. # REEL_PREGEN_MAX_TOOL_ITERS=8 # # On-disk reel cache sweep (runs every 24h, independent of pre-gen). Removes # reel MP4s with no ledger row + no live job that are older than the max age — # i.e. the on-demand cache, which otherwise grows forever. Set to 0 to disable. # REEL_CACHE_SWEEP_ENABLED=1 # Age (days) before an unreferenced reel MP4 is swept. Default 7. # REEL_CACHE_MAX_AGE_DAYS=7