# --- Database --- POSTGRES_USER=vidconf POSTGRES_PASSWORD=vidconf POSTGRES_DB=vidconf DATABASE_URL=postgresql+asyncpg://vidconf:vidconf@localhost:5432/vidconf # --- Redis --- # Обязателен (${REDIS_PASSWORD:?} в docker-compose.yml) — redis запускается # с --requirepass, публикация без пароля = открытый redis в интернет (см. # .forcc/deploy/SESSION2-FINDINGS.md — реальный инцидент на этом проекте). # install.sh генерирует случайное значение при первой установке. REDIS_PASSWORD=change-me-redis-secret # REDIS_URL нужен ТОЛЬКО для запуска backend/worker на хосте вне docker-сети # (см. docs/deploy/dev-setup.md) — внутри контейнеров docker-compose.yml # всегда подставляет redis://:${REDIS_PASSWORD}@redis:6379/0 сам, это # значение игнорируя. Держите пароль в этой строке синхронным с REDIS_PASSWORD. REDIS_URL=redis://:change-me-redis-secret@localhost:6379/0 # --- Seed admin user --- SEED_ADMIN_EMAIL=admin@vidconf.example SEED_ADMIN_PASSWORD=change-me # --- Plugins --- PLUGINS_CONFIG_PATH=config/plugins.yaml # --- Auth (JWT + email-подтверждение) --- JWT_SECRET=change-me-generate-a-long-random-secret ACCESS_TOKEN_TTL_MINUTES=15 REFRESH_TOKEN_TTL_DAYS=14 EMAIL_VERIFICATION_TTL_HOURS=24 FRONTEND_URL=http://localhost:5173 # false только для dev по http://localhost (Safari не сохраняет # Secure-cookie без HTTPS); в проде обязательно true. AUTH_COOKIE_SECURE=false # --- LiveKit (dev placeholders, replace in real .env) --- # Без дефолтов в docker-compose.yml (${VAR:?}) — пустое/отсутствующее # значение остановит `docker compose up` с явной ошибкой, а не тихо # подставит небезопасный дефолт. LIVEKIT_API_KEY=devkey LIVEKIT_API_SECRET=change-me-livekit-secret LIVEKIT_URL=ws://localhost:7880 # URL, который получает браузер клиента для подключения к LiveKit SFU # (backend отдаёт его в join.livekit_url, фронт использует как serverUrl). # ⚠️ КРИТИЧНО для прода на HTTPS: значение ДОЛЖНО быть `wss://<домен>/livekit/` # (проксируется nginx на livekit:7880). Любой `ws://…` на https-странице = # mixed-content → браузер режет соединение, конференция не стартует (Safari/ # Yandex жёстче). `ws://localhost:7880` тут — dev-плейсхолдер; в реальном .env # для vidconf.ru: LIVEKIT_PUBLIC_URL=wss://vidconf.ru/livekit/ LIVEKIT_PUBLIC_URL=ws://localhost:7880 # --- Coturn (dev placeholders) --- TURN_REALM=vidconf.local TURN_STATIC_AUTH_SECRET=change-me-turn-secret # Внешний IP/домен сервера — coturn (network_mode: host) не может определить # его сам. 127.0.0.1 годится только для локальной разработки (без внешних # участников конференции); в проде — реальный внешний IP/домен. # Рендерится в deploy/coturn/turnserver.conf из turnserver.conf.template # скриптом deploy/render-templates.sh (вызывается install.sh). TURN_EXTERNAL_IP=127.0.0.1 # TURN over TLS (5349) — единственный переключатель во всём проекте: пусто = # TLS выключен везде (dev-дефолт, как ниже), непустое значение = coturn # слушает TLS на 5349 (сертификат смонтирован из /etc/letsencrypt через # coturn-certs-init, docker-compose.yml) И LiveKit объявляет клиентам запись # protocol: tls. ⚠️ Обязан быть ДОМЕНОМ сертификата (например, vidconf.ru — # тем же, что и NGINX_CERT_NAME), а НЕ IP-адресом, в отличие от # TURN_EXTERNAL_IP выше: браузер проверяет TLS-сертификат TURN-сервера по # имени хоста, а Let's Encrypt выписывает сертификат на домен. TURN_TLS_HOST= # --- Nginx: TLS (443) + список доменов — deploy/nginx/nginx.conf.template --- # Домены, которые обслуживает nginx (через пробел, все — в server_name). NGINX_SERVER_NAMES=example.com www.example.com # Каталог сертификата в /etc/letsencrypt/live/ — обычно primary-домен # certbot-лайнеджа (SAN может покрывать и остальные домены из списка выше). NGINX_CERT_NAME=example.com # --- LiveKit: ICE/публичный адрес (deploy/livekit/livekit.yaml.template) --- # use_external_ip=false + node_ip=127.0.0.1 — режим для локальной разработки # (подробности — в самом шаблоне). В проде: true + реальный внешний IP/домен. # Рендерится в deploy/livekit/livekit.yaml скриптом deploy/render-templates.sh # (вызывается install.sh). LIVEKIT_USE_EXTERNAL_IP=false LIVEKIT_NODE_IP=127.0.0.1 # --- Транскрибация (профиль compose `transcribe`: egress + worker-transcriber) --- # Общий путь тома `recordings` (egress пишет .ogg-треки, worker-transcriber # читает их для faster-whisper). Внутри docker-сети менять не нужно — # значение по умолчанию (см. deploy/docker-compose.yml) уже совпадает # с точкой монтирования тома в обоих сервисах. RECORDINGS_DIR=/recordings # --- Производительность backend --- # Число процессов uvicorn. Один процесс означает, что любой блокирующий вызов # в обработчике останавливает весь event loop: параллельные входы в конференцию # и WS-чат всех участников встают в очередь. Дефолт 2 рассчитан на 4-ядерный # сервер, где ядра делятся с LiveKit (медиа важнее API). UVICORN_WORKERS=2 # Пул соединений с БД НА КАЖДЫЙ воркер. Общий расход инстанса — # UVICORN_WORKERS × (DB_POOL_SIZE + DB_MAX_OVERFLOW), плюс соединения Celery # и alembic. Держите сумму заметно ниже max_connections у Postgres (по # умолчанию 100), иначе вместо понятной ошибки приложения получите отказ БД. DB_POOL_SIZE=10 DB_MAX_OVERFLOW=10 DB_POOL_TIMEOUT=10 # --- Email (рассылка саммари + .ics-приглашения) --- # `console` — дефолт для dev (письмо только логируется, ссылка подтверждения # email берётся из логов); `smtp` — реальная отправка через aiosmtplib. # Переключатель — только .env (секреты SMTP никогда не попадают в БД/админку). EMAIL_BACKEND=console SMTP_HOST=localhost SMTP_PORT=587 SMTP_USERNAME= SMTP_PASSWORD= SMTP_START_TLS=true SMTP_USE_TLS=false SMTP_FROM=VidConf SMTP_TIMEOUT_S=30 # --- Версия инстанса (релиз v0.0.1) --- # install.sh копирует значение из корневого файла VERSION при каждой # установке/обновлении — руками менять не нужно. VIDCONF_VERSION=0.0.23 # --- Профили compose. Дефолт ниже (`media,monitoring`) — только для ручного # `docker compose up` БЕЗ install.sh: медиа (LiveKit+coturn) + мониторинг, # без ресурсоёмких transcribe/llm (whisper, worker-transcriber, egress, # llama.cpp) — сервисы остаются в docker-compose.yml, просто не в дефолтном # наборе профилей. install.sh ПЕРЕЗАПИСЫВАЕТ это значение по выбранному # пресету инсталлятора (1/2 — media; 3 — +transcribe,llm; 4 — те же, # WHISPER_MODEL/LLM_MODEL_* другого уровня; 5 — +transcribe-gpu,llm-gpu; # monitoring install.sh нигде не включает — поднимайте отдельно, см. # docs/deploy/monitoring.md). --- COMPOSE_PROFILES=media,monitoring # --- Настройки модулей инстанса под пресет --- # install.sh пишет их по выбранному пресету, backend # читает их при первом бутстрапе instance_settings (lifespan) вместо # дефолтного «всё enabled: true» из config/plugins.yaml; те же значения # использует scripts.apply_preset_settings при повторном применении. Пусто — # бутстрап берёт дефолты plugins.yaml (как раньше, для dev без install.sh). --- BOOTSTRAP_CHAT_ENABLED= BOOTSTRAP_TRANSCRIPTION_ENABLED= BOOTSTRAP_AI_LEVEL= # --- Обнаруженное железо (install.sh, ADR-004) — читает # backend/services/ai_levels.py для детекта доступности уровней AI в # админке; НЕ влияет на то, что реально поднимает docker compose # (это делает COMPOSE_PROFILES выше) — используется только для # рекомендации/причин недоступности. HW_CPUS= HW_RAM_MB= HW_GPU_NAME= HW_VRAM_MB= # --- Транскрибация: модель faster-whisper уровня AI (профили `transcribe`/ # `transcribe-gpu`) — small (min) / medium (medium) / large-v3 (max, только # GPU), см. ADR-004. Пишет install.sh по выбранному пресету. --- WHISPER_MODEL=small # --- Суммаризация: модель Qwen3.5 уровня AI (профили `llm`/`llm-gpu`) — # см. ADR-004 и deploy/llm/download-model.sh. Пишет install.sh по выбранному # пресету; значения ниже — дефолт уровня `min`. --- LLM_MODEL_FILE=qwen3.5-4b-instruct-q4_k_m.gguf LLM_MODEL_URL=https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/resolve/main/Qwen3.5-4B-Q4_K_M.gguf LLM_MODEL_MIN_SIZE=2000000000 LLM_TOKENIZER_FILE=qwen3.5-4b-instruct.tokenizer.json LLM_TOKENIZER_URL=https://huggingface.co/Qwen/Qwen3.5-4B/resolve/main/tokenizer.json # --- Мониторинг (профиль compose `monitoring`) --- # Prometheus (9090) и Grafana (3001) публикуются ТОЛЬКО на 127.0.0.1 # (deploy/docker-compose.yml) — с боевого сервера доступ через ssh-туннель, # напр. `ssh -L 3001:127.0.0.1:3001 -L 9090:127.0.0.1:9090 @`, # затем открыть http://localhost:3001 / :9090 у себя локально. То же для # backend (8000, локальный curl-дебаг), livekit signaling (7880, снаружи # работает только через nginx wss://) и llm/llm-gpu (8080/8081). GRAFANA_ADMIN_USER=admin GRAFANA_ADMIN_PASSWORD=change-me-grafana # --- Присутствуют в .env боевого сервера, но НЕ читаются текущим кодом # (backend/frontend) — ни backend/core/config.py, ни frontend/src не # ссылаются на них (проверено grep'ом при харденинге репозитория). Оставлены # здесь только для полноты покрытия реальных ключей сервера; вычистить или # начать использовать — по итогам Сессии 2 (bug hunt). --- MEDIA_URL=http://localhost/media/ VITE_API_URL=http://localhost VITE_LIVEKIT_URL=ws://localhost:7880 NEXT_PUBLIC_LIVEKIT_URL=ws://localhost:7880