- /.well-known/security.txt уходил в SPA-заглушку (200 text/html): добавлена
копия в web/public и явный тип text/plain в статике (RFC 9116, AGENT.md 11.3);
- make security дополнен `npm audit --omit=dev --audit-level=high`;
- новая цель `make sbom` — CycloneDX-отчёт зависимостей (trivy, если есть).
- scripts/test-profiles.sh --env <файл>: профиль берётся из .env, значения
таблицы 10.10 пересчитываются и сверяются с производными ключами
(SQLITE_MMAP_SIZE, MAX_UPLOAD_SIZE, APP_MEMORY_LIMIT, SHARE_SOFT_LIMIT,
BACKUP_KEEP_DAYS и т. д.), offsite-ключи проверяются на наличие.
- make profiles-env — тот же прогон для /opt/glchat/.env.
- На стенде: профиль standard совпал (67 проверок, 0 ошибок); .env дополнен
ключами offsite, установленный backup.sh обновлён, выгрузка и недоступный
приёмник проверены вживую.
- deploy/backup.sh: BACKUP_OFFSITE_TARGET (rsync или rclone:remote:path)
выгружает готовый архив после локальной ротации; неудача выгрузки только
предупреждает. Ротация на приёмнике — опционально
(BACKUP_OFFSITE_KEEP_DAYS, rclone delete --min-age). Имя архива получает
суффикс, если бэкап запущен дважды в одну секунду.
- deploy/install.sh: offsite-настройки в .env (BACKUP_OFFSITE_TARGET,
BACKUP_OFFSITE_KEEP_DAYS) отдельным разделом; детектор незаполненных
плейсхолдеров учитывает цифры в именах (FAIL2BAN_ENABLED); dry-run больше не
требует прав на /opt/glchat — явность GLCHAT_ETC_DIR фиксируется до
подключения common.sh, иначе ветка временного каталога была недостижима.
- scripts/test-profiles.sh: сверка таблицы профилей 10.10 и одинакового набора
PROFILE_* (46 проверок), make profiles-test / ops-test.
- scripts/test-backup.sh: бэкап и offsite на синтетическом инстансе — локальный
rsync-приёмник, шим rclone, недоступный хост, нечисловой срок (12 проверок),
make backup-test.
- scripts/test-install.sh: проверка offsite-ключей в .env после установки.
Миграция 00004: таблицы `messages`, `message_reactions`,
`channel_read_states`, `files`, `invites` и виртуальная таблица `messages_fts`
с триггерами синхронизации (AGENT.md 6.1, 7.6, 7.7, 7.9, 7.15, 7.16).
Полнотекстовый поиск требует сборки с тегом `sqlite_fts5`:
- `GO_TAGS ?= sqlite_fts5` в Makefile (build/test/cover) и в Dockerfile;
- build-tags в golangci-lint;
- страж `internal/database/fts5_guard.go`: сборка без тега падает с понятным
текстом, а не ломается на миграции в рантайме.
- 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