# Antigravity: довести сертификаты до PR и разобрать dependabot

## Состояние

- `main` @ `80b4797` — содержит `fix(enrollment): repair node enrollment path (#24)` от Hermes;
- ветка `antigravity/cert-lifecycle` @ `9d2fe1c` — CI зелёный, с `main` сливается без конфликтов, PR не открыт;
- открыто шесть PR от dependabot: #18–#23.

---

## Часть 1 — доделать `antigravity/cert-lifecycle`

Работа принята по существу: `ClientCertificateDays` в `ControlOptions` с умолчанием 30 и сохранённым `[DynamicDependency]`, `AddYears(1)` заменён на `AddDays(...)`, автопродление Agent при остатке меньше трети с сохранением работы при недоступном Hub, событие `certificate.expiring`, `docs/certificate-rotation.md`, все семь обязательных тестов, 101 тест зелёный. Границы соблюдены — ни одного файла из области Hermes.

Осталось пять пунктов.

### 1.1. Ребейз на текущий `main`

Ветка сделана до merge #24. Конфликтов нет, но база должна быть актуальной: #24 менял привязку конфигурации Agent, а ваша работа трогает `AgentClient.cs`.

### 1.2. Убрать `TEST_EVIDENCE.md` из репозитория

Файл закоммичен в корень. Отчёт — артефакт PR, а не содержимое репозитория. Перенести содержимое в описание PR, файл удалить из ветки.

### 1.3. Проверка на trimmed-артефакте по production-пути

Сделано было так:

```
dotnet publish ... -r win-x64 --self-contained -p:PublishTrimmed=true
.\ochenstarik-smm-control.exe --Control:ClientCertificateDays 999
```

Это командная строка на `win-x64`. В проде конфигурация приходит **переменными окружения** из `control.env` через `EnvironmentFile=` в systemd-юните, на **linux-x64**. Сломанным под триммингом был именно env-путь — в `AgentOptions` не оказалось `[DynamicDependency]`, и Agent молча брал умолчания.

Повторить проверку так, как оно работает в бою:

```bash
dotnet publish src/ServerMonitorManager.Control/ServerMonitorManager.Control.csproj \
  -c Release -r linux-x64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -o /tmp/ctl
Control__ClientCertificateDays=999 /tmp/ctl/ochenstarik-smm-control
```

Ожидается отказ старта с ошибкой валидации. Если сервис стартует — значение не дошло, и настройка в проде работать не будет.

Дополнительно проверить обратное: валидное значение из переменной окружения действительно применяется к выпускаемому сертификату, а не подменяется умолчанием 30. Например `Control__ClientCertificateDays=45`, выпустить сертификат, посмотреть `notAfter`.

Вывод обеих проверок приложить к PR.

### 1.4. Убрать дублирующий эндпоинт продления

В `Control/Program.cs` появились два пути продления:

- `agents.MapPost("/certificate/renew")` — в группе с `RequireAuthorization("Agent")`, всё правильно;
- `app.MapPost("/api/v1/certificates/renew")` — без `RequireAuthorization`, отсекает чужих случайной проверкой на пустой `entityId` и разветвляется по claim роли внутри.

Второй ведёт себя fail-closed, но держится не на конвейере авторизации, а на том, что у неаутентифицированного запроса не окажется claims. Это не соглашение проекта: рядом в том же файле все защищённые эндпоинты объявлены через группы с политикой.

Выбрать одно:

- **предпочтительно** — удалить `app.MapPost("/api/v1/certificates/renew")`, оставив продление Agent в группе `agents`, а продление Operator добавить в группу `control` с `RequireAuthorization("Operator")`;
- либо оставить один общий эндпоинт, но объявить его через группу с политикой, требующей аутентифицированную identity, и разветвление по роли выполнять уже внутри.

Добавить тест: запрос на продление **без клиентского сертификата** отклоняется, и отклоняется конвейером, а не обработчиком.

### 1.5. Открыть PR

После пунктов 1.1–1.4. Описание PR заполнять **после** завершения CI, со ссылками на конкретные прогоны.

---

## Часть 2 — dependabot, отдельным PR каждый

Шесть открытых PR. Сливать пачкой нельзя: три из них меняют то, от чего зависит воспроизводимость и цепочка поставки.

### 2.1. Безопасные — слить после зелёного CI

- **#22** `Microsoft.NET.Test.Sdk` 18.0.1 → 18.8.1;
- **#19** `actions/setup-dotnet` 5.4.0 → 6.0.0;
- **#18** `softprops/action-gh-release` 2.6.2 → 3.0.2.

Для двух последних убедиться, что dependabot заменил SHA, а не вернул плавающий тег: инвариант «ни одного плавающего тега в `uses:`» из PR #14 должен сохраниться. Проверять на каждом PR:

```bash
grep -rn 'uses:.*@v[0-9]' .github/workflows/ && echo "НАЙДЕН ПЛАВАЮЩИЙ ТЕГ" || echo "все запинены"
```

`action-gh-release` 3.0.x — мажорный скачок, посмотреть changelog на предмет изменения имён входных параметров: он используется в release-workflow, а те **не запускаются на pull request**, поэтому CI поломку не покажет. Это ровно тот класс дефектов, что вы уже ловили в PR #14 с `permissions`. Проверять `actionlint`, а не полагаться на зелёный PR.

### 2.2. Требуют внимания

- **#21** `actions/checkout` 6.1.0 → 7.0.1 и **#20** `actions/upload-artifact` 6.0.0 → 7.0.1 — мажорные. `upload-artifact` v7 может менять поведение при отсутствии файлов и формат имён артефактов; в `linux-release.yml` и `windows-release.yml` от имён артефактов зависит сборка релиза. Проверить, что после обновления релизные ассеты собираются с теми же именами: их знает установщик `smm-setup.sh` и bootstrap.

### 2.3. Не сливать без отдельной проверки

- **#23** `SQLitePCLRaw.bundle_e_sqlite3` 2.1.12 → **3.0.5**.

Это мажорное обновление нативной библиотеки под SQLite, на которой держится вся база Control: enrollment, identities, links, provisioning, audit. Требуется:

- прогон полного Control suite на Linux, не только на Windows;
- проверка миграций на **существующей** базе: взять `control.db`, созданную текущей версией, открыть новой сборкой, убедиться, что `PRAGMA user_version` и данные читаются;
- проверка `backup-create` и `backup-restore` на этой же базе;
- проверка, что self-contained trimmed-публикация под `linux-arm64` не ломается — нативные библиотеки в trimmed single-file собираются иначе, чем под x64.

Если хоть одна проверка не выполнена, PR не сливать и написать это прямо. Обновление некритично и подождёт.

---

## Порядок

Часть 1 → 2.1 → 2.2 → 2.3. Сертификаты первыми: это пункт Горизонта 0, а обновления зависимостей подождут.

## Отчёт

Раздельно: что проверено локально, что в CI со ссылками, что не проверялось и почему. По части 2 — по каждому PR отдельной строкой с вердиктом «слит / отложен / отклонён» и причиной.
