.page-head h1 (общий заголовок оболочки) — тот же баг с фиксированным
кеглем 34px, что и в auth.css, чинится тем же способом (clamp), лечит заодно
любую другую длинную шапку на страницах оболочки. В админке дополнительно:
поясняющий текст под заголовком скрыт на мобильном (не несёт функции), 4
вкладки переносятся в 2 ряда вместо горизонтального оверфлоу всей страницы.
room-toolbar с 8 кнопками (520px) не помещался в мобильную ширину (375px) —
крайние кнопки обрезались без возможности до них доскроллить. На мобильном
убираем демонстрацию экрана/полноэкранный режим/мини-окно (редко нужны с
телефона) и уплотняем оставшиеся 5 кнопок. В топбаре убираем инвайт-чип
(номер + копирование ссылки) — сверху остаются только название конференции
и статус.
font: var(--text-display-lg) даёт фиксированные 34px — слово не помещалось
в брендовую панель на узких экранах (375px), а overflow:hidden панели
обрезал его вместо переноса. font-size: clamp() уменьшает кегль на узких
вьюпортах вместо обрезки.
Поднята версия до 0.0.2 (VERSION, .env.example, docker-compose дефолт).
Добавлен CHANGELOG.md с описанием изменений 0.0.1 → 0.0.2: харденинг
безопасности (порты на loopback, redis requirepass), перенос конфигов
деплоя в репозиторий (TLS-443, шаблоны, --env-file), fail-fast install.sh,
руководство docs/deploy/DEPLOYMENT.md.
Воспроизводимый путь от голой Ubuntu 24 до рабочего https://<домен>:
предусловия (DNS/firewall/место на диске), выпуск SSL (bootstrap
самоподписанным сертификатом → install.sh → certbot --webroot +
deploy-hook на автопродление), полный разбор обязательных значений .env,
профиль monitoring, чек-лист проверки после деплоя, TURN как опциональный
раздел для экстремального NAT (по итогам реального кросс-сетевого
тестирования — не обязателен), обновление/редеплой, бэкап и restore БД,
траблшутинг по реальным инцидентам проекта, полный разбор мониторинга
(доступ через SSH-туннель, панели дашборда с порогами тревоги, runbook
включения транскрибации/суммаризации).
Ссылки на документ добавлены в README.md: врезка под «Быстрый старт» и
приоритетная ссылка в разделе «Развёртывание».
FastAPI отдаёт /openapi.json на корневом пути (маршруты backend объявлены
с явным префиксом /api/... в декораторах, а не через APIRouter(prefix=)),
поэтому запрос попадал в catch-all location / (раздача index.html фронта)
раньше, чем мог бы дойти до backend — снаружи схема API была недоступна,
что мешало отладке.
Добавлен точный location = /openapi.json, проксирующий на backend:8000
(матчится раньше catch-all за счёт приоритета exact-match в nginx).
set_env_var COMPOSE_PROFILES перезаписывает значение целиком по пресету,
затирая media,monitoring из .env.example — Grafana/Prometheus оставались
не поднятыми, пока оператор не допишет профиль в .env вручную.
--monitoring дописывает monitoring к профилю, который выставил пресет,
и включает его сразу в тот же docker compose up (не только в .env).
На свежем клоне install.sh молча принимал непустые dev-плейсхолдеры
.env.example (LIVEKIT_PUBLIC_URL=ws://localhost:7880, NGINX_SERVER_NAMES=
example.com, LIVEKIT_API_KEY=devkey и т.д.) — ensure_secret/ensure_default
генерируют значения только для ПУСТЫХ ключей. Итог: рапорт об успехе,
на деле — mixed-content и nginx на несуществующем домене.
validate_prod_env проверяет согласованность .env, когда NGINX_SERVER_NAMES
указывает на реальный домен (не localhost) — останавливает установку
понятной ошибкой, если что-то из прод-обязательных значений осталось
плейсхолдером.
Redis without a password was the root cause of the security incident
(cron miner via unauthenticated replication RCE, see
.forcc/deploy/SESSION2-FINDINGS.md). Loopback binding alone doesn't
protect against a compromised container inside the same compose
network, so wire REDIS_PASSWORD as a required secret everywhere redis
is used: backend/worker/worker-transcriber(-gpu), redis-exporter,
livekit and egress (via rendered templates). docker compose now
refuses to start without it instead of silently running unauthenticated.
Close host-published ports that don't need to be reachable from outside
the docker network: nginx already proxies backend/livekit by service
name, and admin UIs (prometheus/grafana) and llm servers should only be
reachable via SSH tunnel. Only 80/443/7881 + the WebRTC UDP range stay
open to the internet.
The client receives this value as the LiveKit serverUrl (via join.livekit_url).
A ws:// value on an https page is blocked as mixed content, so conferences
never connect — the exact failure hit on vidconf.ru, where the runtime value
had fallen back to the ws://localhost:7880 default. Spell out the prod
requirement (wss://<domain>/livekit/) next to the placeholder.
Both are only used inside the compose network (services reach them by name,
postgres:5432 / redis:6379). Publishing on 0.0.0.0 exposed them to the
internet — Docker's DNAT rules bypass ufw, so the ports were reachable
despite the firewall having no allow rule for them. Bind the published
ports to 127.0.0.1 so external access requires an SSH tunnel.
Целевой набор профилей compose по умолчанию (media,monitoring) не включает
llm/llm-gpu — job `llm` в prometheus.yml и алерт LlmDown в alerts.yml
закомментированы, иначе Prometheus постоянно логировал бы неудачный скрейп
несуществующего таргета, а LlmDown вечно оставался бы firing. Раскомментировать
оба вместе — на инсталляциях с профилем llm/llm-gpu (пресеты 3-5 install.sh).
Дашборд «Пайплайны пост-обработки» и docs/deploy/monitoring.md обновлены с
пояснением, что большинство панелей показывают «No data» без
transcribe/llm — это ожидаемо, не баг.
Добавлены плейсхолдеры для всех переменных, введённых хардненингом деплоя
(NGINX_SERVER_NAMES/NGINX_CERT_NAME, LIVEKIT_USE_EXTERNAL_IP/LIVEKIT_NODE_IP,
TURN_EXTERNAL_IP) — generic-значения (example.com/127.0.0.1/false), реальные
домены/IP боевой инсталляции задаются в .env вручную и install.sh их не
перезапишет.
COMPOSE_PROFILES по умолчанию — media,monitoring (только для ручного
`docker compose up` без install.sh): убирает ресурсоёмкие transcribe/llm
из дефолтного набора, install.sh продолжает выставлять профили по
выбранному пресету инсталлятора независимо от этого дефолта.
Добавлены MEDIA_URL/VITE_API_URL/VITE_LIVEKIT_URL/NEXT_PUBLIC_LIVEKIT_URL —
присутствуют в .env боевого сервера, но не читаются текущим кодом
(задокументировано как есть, для полноты покрытия реальных ключей).
docker compose определяет .env для подстановки ${VAR} по каталогу
compose-файла (deploy/), а не по текущей директории — repo-root .env,
который использует install.sh и вся документация, молча не подхватывался.
Это и была причина "WARN: LIVEKIT_API_KEY not set" на боевом сервере:
секреты были в .env, но compose их не видел и подставлял небезопасные
дефолты (см. таблицу разведки сервера).
Теперь ставшие обязательными ${VAR:?} в docker-compose.yml (см. предыдущий
коммит) без этого немедленно проваливали бы конфиг на любой из
документированных команд. Добавлен `--env-file .env`/"$ENV_FILE" ко всем
вызовам docker compose в install.sh и в командах из README/docs.
install.sh дополнительно: ensure_default (аналог ensure_secret без генерации
секрета) для новых не-секретных параметров nginx/coturn/livekit
(NGINX_SERVER_NAMES, NGINX_CERT_NAME, LIVEKIT_USE_EXTERNAL_IP,
LIVEKIT_NODE_IP, TURN_EXTERNAL_IP) — дефолты только для локальной
разработки, не перезаписывают значения, заданные вручную на боевом
сервере. Плюс вызов deploy/render-templates.sh перед сборкой/подъёмом
стека.
Переносит рабочие правки продакшен-сервера в репозиторий воспроизводимо:
- nginx: HTTPS-блок на 443 (TLS1.2/1.3, http->https redirect, ACME
webroot, X-Forwarded-Proto) добавлен в nginx.conf.template. Список
доменов и имя каталога сертификата — параметры (NGINX_SERVER_NAMES,
NGINX_CERT_NAME), не хардкод. Рендерится штатным entrypoint'ом образа
nginx (envsubst-on-templates).
- Самоподписанный dev-сертификат генерируется на старте контейнера
(docker-entrypoint-certs.sh), если реальный Let's Encrypt не смонтирован
— иначе `docker compose up` без профиля ломался бы локально без
реальных сертификатов.
- healthcheck nginx переключён на /healthz (без TLS-редиректа), иначе
http://127.0.0.1:80/ после добавления 443-редиректа всегда unhealthy.
- coturn/turnserver.conf и livekit/livekit.yaml переведены в *.template —
оба образа не читают env для статических полей (static-auth-secret,
use_external_ip/node_ip, webhook.api_key), поэтому реальные файлы
генерируются перед стартом стека скриптом deploy/render-templates.sh
(вызывается install.sh). Секреты/IP — только в .env, не в git.
- docker-compose.yml: LIVEKIT_API_KEY/SECRET везде (backend, livekit,
egress) через ${VAR:?} без небезопасных дефолтов; порт 443 + монтирование
/etc/letsencrypt (ro) и certbot-webroot у nginx; build-args
VITE_LIVEKIT_URL/NEXT_PUBLIC_LIVEKIT_URL для frontend (сейчас не
используются кодом, оставлены про запас).