v1.2.0
2026-07-12
● stable
Что изменилось
Added
- **`secrets rotate SECRET_KEY --reencrypt` — безопасная ротация.** Останавливает Gitea, бэкапит базу (sqlite: копия файла рядом с оригиналом, `.reencrypt-bak`; mysql/postgres: страховкой служит единая транзакция перешифровки), записывает новые `secrets.ini`/`app.ini`, перешифровывает каждую затронутую строку — проверяя `decrypt(new) == plaintext` для каждой *до* записи, — затем запускает Gitea и проверяет здоровье. Любой сбой автоматически откатывает всё: базу, `secrets.ini`, `app.ini`, рестарт. Gitea никогда не видит рассинхрона «новый ключ / старый шифротекст». Обе схемы шифрования Gitea реализованы байт-в-байт (сверено с закреплённым исходником v1.26.4), покрыты все пять зашифрованных мест. Поддержанные движки: `sqlite3`, `mysql`, `postgres` (`mssql` отклоняется с понятной ошибкой). Повторный запуск всегда безопасен: каждый запуск мигрирует то, что расшифровывается под текущим ключом, и никогда не трогает строки, которые прочитать нельзя.
- **`secrets rotate SECRET_KEY --dry-run`** — классифицирует каждую зашифрованную строку (будет перешифрована / уже мигрирована / нечитаема) и выводит счётчики по местам и ID строк, ничего не меняя. Не требует `--yes`.
- **`--orphan-encrypted-data-i-understand`** — явное осознанное согласие для операторов, которые и так собираются перевыпустить секреты и перезавести 2FA вручную.
- **`doctor` предупреждает, когда `SECRET_KEY` пуст или равен публичному дефолту Gitea.** Пустой `[security] SECRET_KEY` означает, что Gitea шифрует значением, зашитым в её собственный исходный код, — данные фактически не защищены. Проверка называет безопасный путь исправления (`secrets rotate SECRET_KEY --reencrypt`) и никогда не печатает настоящий ключ оператора.
Changed
- **Изменение поведения (safety): `secrets rotate SECRET_KEY` отказывается сиротить данные.** Раньше ротация применялась чисто и рапортовала успех, молча осиротив каждую зашифрованную строку — продовый инцидент, из которого вырос этот релиз. Теперь она жёстко останавливается *до записи любого файла*, если существуют данные под текущим ключом, печатает по категориям, сколько будет потеряно, и требует либо `--reencrypt` (мигрировать), либо `--orphan-encrypted-data-i-understand` (принять потерю). Ротация `INTERNAL_TOKEN` / `JWT_SECRET` / `LFS_JWT_SECRET` не изменилась. Строго говоря — изменившийся исход существующего вызова; выпущено в минорном релизе сознательно, потому что прежним исходом была потеря данных.
- **Бинарь вырос на ~7 МБ** (≈19,6 → ≈27 МБ): gtcnsl теперь несёт те же CGO-free драйверы БД, что использует сам Gitea (`modernc.org/sqlite`, `go-sql-driver/mysql`, `lib/pq`), чтобы читать и перешифровывать базу внутри процесса — plaintext никогда не покидает процесс gtcnsl, клиентские утилиты на хосте не нужны.
Fixed
- Описание команды `secrets` больше не утверждает, что ротация «(in v0.4)» — она в деле с тех пор, как v0.4 реально вышла.
Загрузка
Выберите архитектуру. Прямые ссылки ведут на ассеты релиза.
$ curl -fsSL https://dl.gtcnsl.ru/v1.2.0/gtcnsl-v1.2.0-linux-amd64 -o /usr/local/bin/gtcnsl && chmod +x /usr/local/bin/gtcnsl
i
Проверка
Сверьте с подписанным манифестом сумм, прежде чем что-то запускать.
checksums.txt