- переход на chi + huma (решение D-006): huma отдаёт типизированные ручки и
генерирует документ, auth-ручки живут на chi (cookie и заголовки напрямую)
- единый формат ошибок: код (auth.invalid_credentials, auth.2fa_required,
perm.denied и т.д.) + человекочитаемое сообщение (AGENT.md 8.5)
- cookie сессии __Host-session: HttpOnly, SameSite=Lax, Secure при TLS;
альтернатива — Bearer-токен для desktop/CLI (AGENT.md 8.1)
- ручки: register, login, logout, logout-all, sessions, step-up, 2fa/setup,
2fa/enable, users/@me; IP и User-Agent прокидываются из запроса в контекст
- /api/v1/openapi.json: объединённый документ (схемы huma + контракт auth)
- тесты: регистрация через API с cookie, ошибки входа, обязательная сессия,
валидация, наличие всех путей в OpenAPI
- регистрация: политика пароля (≥10 + локальный словарь 10k утечек, SecLists MIT),
username и email без учёта регистра, шифрование email с привязкой к аккаунту,
blind index для поиска, автовступление в главный сервер с ролью @user
- вход: Argon2id+pepper, TOTP при включённом 2FA, блокировка после серии неудач,
события безопасности; единый ответ на неверный пароль и неизвестный аккаунт
- сессии: opaque-токены (в БД только SHA-256), ротация токена, logout/logout-all,
отзыв остальных сессий при смене пароля, step-up с окном 10 минут
- 2FA: настройка секрета с QR-URL, подтверждение кодом, 8 резервных кодов
(хранятся хэшами), обязательность для инстанс-админов, запрет отключения
- тесты на реальной SQLite: регистрация/вход/дубликаты, слабые и утёкшие пароли,
ротация и logout-all, TOTP и резервные коды, step-up, смена пароля,
выключенная регистрация, отсутствие секретов в логах
- internal/crypto/password.go: Argon2id с параметрами под 1 vCPU (OWASP),
обязательный pepper (HMAC-SHA-256 до Argon2id), разбор формата хэша,
NeedsRehash для калибровки параметров
- internal/crypto/secretbox.go: AES-256-GCM с версионированным ключом (v1:),
разделение мастер-ключа на подключи шифрования и blind index (HMAC-SHA-256),
blind index для поиска по email без расшифровки (§9.2)
- internal/crypto/tokens.go: opaque-токены сессий (хранится только SHA-256),
резервные коды 2FA, нормализация email, политика пароля и username
- тесты: соль, pepper, версии ключа, чужие шифротексты, стабильность blind index
- go.mod: добавлены chi v5, huma v2, pquerna/otp (подключаются следующими шагами)
Если задан --turn-relay-host, turn.domain указывает на релей, а не на
turn.<domain>: иначе клиенты получали недостижимый TURN-кандидат
(порт 3478 на VPS обслуживает coturn, а не домашний LiveKit).
Также добавлена smoke-проверка обновления сертификата TURN.
Правила блокировали 8080/7880 для всех, кроме docker-сетей, поэтому внешний
прокси не мог дойти до приложения. Теперь разрешена подсеть --allow-from,
правила снимаются перед повторным применением (не дублируются).
Условия и подстановки внутри heredoc выполнялись shell при генерации файла
и попадали в .env как код — docker compose не мог его прочитать.
Теперь шаблон пишется с quoted heredoc, значения подставляются sed.
- compose: app и LiveKit публикуются на GLCHAT_APP_BIND/GLCHAT_LIVEKIT_BIND
(по умолчанию 127.0.0.1, для схемы «VPS-прокси + домашний сервер» — адрес в WireGuard)
- install.sh: --app-bind-addr/--livekit-bind-addr, --turn-cert-remote* для
загрузки сертификата по SSH, glchat-cert-refresh умеет тянуть его с внешнего узла
- install.sh: --tls-terminated-by-proxy (ссылки https/wss без своего 80/443),
--turn-relay-host/--turn-relay-port/--turn-relay-tls-port, общий TURN_SECRET
для coturn (use-auth-secret) и LiveKit
- livekit.yaml.tpl: rtc.turn_servers с внешним релеем (udp + tls)
- секрет вычисляется один раз и сохраняется в .env
- проверяем /proc/swaps, а не только имя /swapfile
- rm -f перед созданием и мягкая обработка ошибки swapon
- на VPS с /swap.img установщик больше не падает с 'Text file busy'
На общем сервере (VPS владельца) ufw reset удалял правила WireGuard и
wstunnel. Теперь перед сбросом снимается снимок правил без комментария
glchat, а после настройки они восстанавливаются.
- при установке без TLS (--skip-tls, стенд без домена) клиент получает
http:// и ws:// вместо неработающих https:// и wss://
- установщик пишет TLS_ENABLED в .env; тесты фиксируют обе схемы
- убраны недопустимые поля (audio.min_level, audio.smooth_intervals,
video.dynacast, video.disable_simulcast) — из-за них контейнер livekit
циклически перезапускался
- набор кодеков задаётся через room.enabled_codecs: VP8 на lite/standard,
VP8+VP9+H.264+AV1 на plus/pro
- keys: {} — ключи передаются через LIVEKIT_KEYS (секрет не попадает в файл)
- smoke.sh: ветка --skip-tls больше не проверяет HTTPS, бэкап с расширением
.age больше не помечается как незашифрованный
- Dockerfile: musl-tools + полная статическая линковка, рантайм на alpine 3.20
(debian-slim сам весит 113 MB, бюджет §9.1 <50 MB с ним недостижим)
- измерено на HomeServer: образ 30.2 MB, RSS 3.6 MB, холодный старт 0.21 с
- install.sh: set_data_ownership приводит data/{db,files,backups} к 10001:10001;
без этого контейнер не может создать БД (проверено e2e)
- docs/DECISIONS.md: D-006 (временный stdlib-роутер до Фазы 1), D-007 (musl+alpine),
D-008 (владение каталогами данных)
- рантайм не тянет ca-certificates/tzdata/curl: bundle сертификатов копируется
из официального alpine-образа, healthcheck выполняет сам бинарник
- cmd/glchat: подкоманда healthcheck (HTTP-запрос к /healthz с таймаутом)
- compose: APP_VERSION передаётся в environment (версия видна в meta без пересборки)
- бюджет §9.1: базовый слой debian-slim + 9.2 MB бинарник + 356 KB клиент
- пути томов и профиль Caddy задаются как GLCHAT_DATA_DIR/GLCHAT_CADDY_PROFILE
(внутренний DATA_DIR=/app/data остаётся в environment сервиса app)
- значения записываются в .env, поэтому docker compose config проходит без
предупреждений "variable is not set"
- install.sh: флаг --skip-image для прогона в DinD, где сборка невозможна
(overlay поверх overlay), с пропуском pull/up/smoke
- scripts/test-install.sh: полный набор проверок артефактов (compose config -q,
Caddyfile, livekit.yaml, nftables, fail2ban, systemd-юниты, UFW, jails)
resolve_derived_values() вычисляет CADDY_PROFILE, LIVEKIT_PUBLISH,
TURN_EXTERNAL_TLS, PROFILE_LIVEKIT_DISABLE_SIMULCAST и
PROFILE_LIVEKIT_USE_EXTERNAL_IP после выбора профиля и до write_env_file:
устраняет падение set -u на PROFILE_LIVEKIT_SIMULCAST_DISABLED.
- resolve_derived_values(): производные значения (files.<domain>) считаются
до записи .env; раньше set -u ронял установщик на write_env_file
- убраны local-объявления, маскировавшие turn_external_tls/use_external_ip/
disable_simulcast/compose_template внутри render_all_templates
- из рендера compose убрана неиспользуемая пара IMAGE_TAG
- guard чётности аргументов рендера пишет в журнал, а не в консоль:
проверка результата — отсутствие незаполненных плейсхолдеров
- render_template: значения передаются через окружение (envsubst), а не
подставляются в variadic-аргументы — многострочные значения больше не
ломают разбор, неполный список не уводит bash в бесконечный цикл
- export "$pair": bash сам разбирает KEY=value (было export "${pair}")
- шаблоны переведены на ${VAR}-синтаксис, добавлен compose.test.yaml.tpl
для тестового режима установщика (--skip-tls без публикации портов)
- dry-run не требует root и работает вне Linux (проверка ОС пропускается)
- install.sh: учитываются --livekit-*-порты и --turn-*-порты в livekit.yaml,
исправлено экранирование апострофа в итоговой сводке
- scripts/debug-install.sh: трассировка установщика в контейнере
- .golangci.yml: набор линтеров под требования §14 (gosec, gocritic, sloglint,
noctx, revive, depguard для запрета циклических зависимостей internal/*)
- gofumpt вынесен из formatters golangci-lint v2.13 (сообщал о расхождениях,
которые сам же не устранял) и применяется отдельным инструментом
- make fmt/go-lint: проверка gofumpt + golangci-lint fmt --diff
- scripts/gitleaks-scan.sh: поддержка gitleaks 8.19+ (dir) и старых версий (detect)
- исправлены замечания линтеров: обработка ошибок Close, контекст в логах,
NewRequestWithContext в тестах, комментарий к blank-import драйвера SQLite
- структура по AGENT.md §12: cmd/, internal/, web/, deploy/, queries/, docs/
- LICENSE: полный текст AGPL-3.0-or-later
- NOTICE: атрибуция референсов (Spacebar, Fermo) и сторонних компонентов
- SECURITY.md и .well-known/security.txt: правила раскрытия уязвимостей
- .env.example: все переменные окружения с дефолтами
- .gitignore/.dockerignore/.editorconfig/.gitattributes/.gitleaks.toml