# Server Monitor Manager — актуальная доска

Дата фиксации: 2026-08-18.

## Подтверждённый baseline

Проверено по `origin/main` и GitHub, а не по отчётам.

- `main`: `7c43977` — merge PR #61.
- Открытых PR нет.
- Последний **опубликованный** релиз: `v0.1.0-alpha.18`.
- Сожжённые номера без релиза: `alpha.10`, `alpha.11`, `alpha.19`.
- Всё, что накоплено в `main` после alpha.18 — установщик с удалением, права на mesh, веб-консоль, вход по паролю — на серверы ещё не доехало.

## Бэклог

- **Ubuntu 26.04.** `validate_platform` пропускает только 22.04/24.04 и Debian 12/13. 26.04 — текущий LTS с апреля 2026 и версия WSL владельца, то есть без поддержки теряется машина для репетиции установки. Нужны гейт, `docs/linux-platform-matrix.md` и матрица CI; раннера `ubuntu-26.04` у GitHub может ещё не быть, тогда потребуется VM-сборка.
- **`flock` на `nodes.tsv`.** Ни bash, ни Control не берут блокировку. Внутри Control выдача сериализована семафором процесса, но с путём `node-code` он не согласован. Пока код выдаётся из одного места — гонка теоретическая.

## Состояние Горизонта 0

Закрыт кодом полностью:

| Пункт | |
|---|---|
| Реконсиляция Links от фактического состояния | ✅ |
| Регистрация Node | ✅ |
| Сертификаты: срок, автопродление, ротация CA | ✅ |
| Роль Monitor и контракт снимка | ✅ |
| Подписанная поставка: manifest v2, cosign, проверка в bootstrap и Desktop | ✅ |
| Политика релизов: неизменяемый тег, единственный публикатор | ✅ |
| Воспроизводимость сборки, lock-файлы, пиннинг Actions | ✅ |
| Сравнение версий при обновлении | ✅ |
| **Физическая приёмка на реальной топологии** | 🟡 |

Физическая приёмка — единственный пункт, не закрываемый кодом. Состояние на 2026-08-16:

- **Hub** `178.212.13.102` (`host1885995-3`, Ubuntu 24.04 x86_64) — поставлен из `v0.1.0-alpha.14`. Подпись manifest проверена на самом сервере, `healthz` отвечает `Healthy`, mesh `10.77.0.1/24`;
- **Node `ai-agent`** `vm1957808` (сервер с 3x-ui) — зарегистрирован, `10.77.0.2`, handshake свежий, ping 7.6 мс, трафик в обе стороны;
- **свободных серверов больше нет.** У владельца остались две машины WSL с Ubuntu 26.04, которую установщик не поддерживает. `tests/acceptance/three-server-mesh.sh` требует Hub плюс три Node: узел-источник и две цели (`HOME_WG_IP`, `SECOND_WG_IP`) — семантика Links проверяется тем, что к одной цели доступ разрешён, а к другой запрещён. На двух машинах эта проверка невыполнима.

Правила nftables проверены на живом Hub: таблица `inet ochenstarik_smm` цепляется только к хуку `forward` и только к трафику `smm0 → smm0`, политика `accept`, хука `input` нет. Установка на серверы с 3x-ui безопасна.

## Активные задачи

Задания переписаны 2026-08-18. Прежние файлы начинались с описания уже сделанного, и исполнитель принимал это за отчёт о выполнении: Codex вернул «задание уже выполнено, PR #58, f7b10ac» вместо работы над невыполненной частью. Новые файлы содержат только предстоящую работу; сделанное живёт в этой доске, а не в задании.

1. `smm-codex-task-release-alpha20-2026-08-18.md` — Codex: закрыть дыру в репетиции выпуска, привести зашитые версии в согласие, записать судьбу `alpha.19`, выпустить `v0.1.0-alpha.20`.
2. `smm-antigravity-task-console-links-2026-08-18.md` — Antigravity: управление Links и журнал событий в консоли. Сейчас Links можно только смотреть; создать или отозвать нельзя, то есть главной функцией продукта из интерфейса пользоваться невозможно. Новых эндпоинтов не требуется — `POST /links`, `POST /links/{id}/disable` и `GET /events` уже есть.
3. `smm-cleanup-and-install-2026-08-15.md` — владелец: физическая приёмка. Единственный пункт Горизонта 0, не закрываемый кодом.

## Сожжён v0.1.0-alpha.19

Тег создан на `7c43977`, `Release pipeline` упал, релиз не опубликован, номер не переиспользуется — третий такой случай после `alpha.10` и `alpha.11`.

Причина: шаг `Package bootstrap` сверяет `DEFAULT_RELEASE_TAG` в `deploy/smm-setup.sh` с выпускаемым тегом. В `main` стоял `v0.1.0-alpha.18`, тег ставился `alpha.19`, `grep -Fq` не нашёл совпадения, `set -e` уронил шаг. Сама проверка правильная: она не даёт выпустить установщик, по умолчанию тянущий ассеты чужого релиза.

**Дыра в процедуре, а не в проверке.** Весь блок закрыт условием `refs/tags/*`, поэтому пробный прогон через `workflow_dispatch` идёт по `refs/heads/main` и эту сверку пропускает. Репетиция перед тегированием, введённая именно для того, чтобы не сжигать номера, не покрывает единственный шаг, который на теге и ломается. Пока это так, каждый выпуск остаётся лотереей — что и подтвердилось: репетиция была зелёной, тег упал.

## Сделано и слито

- **PR #53** — эндпоинт `POST /api/v1/control/agents/{nodeId}/enrollment-code`, роль `Operator`, код `SMMNODE2` формата, совместимого с установленным bootstrap;
- **PR #55** — проверка подписи в Desktop против настоящего релиза, сетевые тесты отделены категорией, шаг live-release нацелен на `v0.1.0-alpha.18`;
- **PR #57** — Control получил доступ к состоянию mesh: `$MESH_DIR` стал `root:ochenstarik-smm-control` `0770`, файл `0660`, права выставляются в единственном месте через `ensure_mesh_state`, существующие установки чинятся при обновлении, `$WG_DIR` с приватными ключами остался `0700 root:root`;
- **PR #58** — интерактивный установщик: запуск без аргументов, выбор Hub или узла, показ состояния машины, зависимости, разбор кода, отказ без tty;
- **PR #59** — веб-консоль оператора: узлы, Links, выдача кода регистрации с отпечатком CA и обратным отсчётом. Ассеты отдаются явными маршрутами в группе `RequireAuthorization("Operator")`, а не через `UseStaticFiles`, поэтому закрыт и интерфейс, а не только API. Шесть тестов: анонимный отклонён, `Agent` и `Automation` запрещены, `Operator` получает HTML и ассеты, HTML содержит предупреждение о сверке отпечатка, и отдельно — ассеты достаются из ресурсов сборки при отсутствии `wwwroot` на диске. Последнее существенно: Control публикуется как единый файл, и на сервере работает именно эта ветка.

- **PR #60** — режим полного удаления: третий пункт выбора роли, план перед подтверждением, подтверждение словами `UNINSTALL` и `DESTROY-DATA`, порядок «остановить службы → снять процессы TERM/KILL с проверкой → сеть → файлы → учётные записи», две глубины, четыре проверочные строки в конце. Порядок вызовов сверяется тестом по номерам строк, а смоук в контейнере создаёт постороннего пользователя и посторонний юнит и убеждается, что они переживают удаление;
- **PR #61** — вход по логину и паролю для тестов: выключен по умолчанию, предупреждение в логе при каждом старте, отдельная политика частоты `password-login`, роль строго `Operator`, приоритет клиентского сертификата, отзыв сессии при выходе. Семь тестов. Вместо Argon2id или bcrypt сделан PBKDF2-SHA256 с 600 000 итераций — отступление от буквы задания, принято: это рекомендация OWASP для данного семейства, реализация встроена в .NET, а сторонний крипто-пакет в проекте с lock-файлами и публикацией единым trimmed-файлом стоит дороже пользы.

## Проверка подписи доказана с обеих сторон

`smm-antigravity-task-desktop-update-proof-2026-08-13.md` выполнен и убран в архив 2026-08-18, PR #55, `main` @ `8af8384`.

- приёмочный тест и четыре негативных случая работают на настоящих manifest, подписи и сертификате опубликованного релиза, загрузка публичным HTTP без `gh` и без токена;
- исполнение в Windows CI доказано **намеренной поломкой**: красный прогон 32049692162, затем откат и зелёные. Единственный случай в проекте, когда утверждение об исполнении подкреплено падением;
- сетевые тесты отделены: `Test Desktop security` идёт с `--filter Category!=LiveRelease`, отдельный шаг `Test Desktop security (live release)` — с `Category=LiveRelease`. Основной гейт больше не зависит от доступности github.com, офлайн-прогон разработчика зелёный;
- шаг live-release нацелен на `SMM_TEST_RELEASE_TAG: v0.1.0-alpha.18`, то есть проверяется релиз, который получают пользователи. Исполнение и успех подтверждены прогоном 32053732808.

Вместе с закрытым заданием alpha.15 это означает: подписанная поставка проверяется на настоящем материале и на стороне Linux, и на стороне Windows, автоматически, на каждом релизе.

## Подписанная поставка закрыта

`smm-codex-task-alpha15-2026-08-15.md` выполнен целиком и убран в архив 2026-08-18:

- установщик обеспечивает cosign сам: `COSIGN_VERSION="v3.1.3"`, контрольные суммы закреплены константами на обе архитектуры (`ochenstarik-server-monitor-manager.sh:29-31`, функция `ensure_cosign`);
- шаг `Setup cosign` из `release-verification.yml` удалён, поэтому проверка проходит только когда cosign обеспечивает сама поставка. Проверяется продукт, а не окружение GitHub;
- автозапуск переведён на `workflow_run` по завершении `Release pipeline` и **срабатывает**: прогоны 16:54, 17:20 и 17:43 от 2026-08-17 запущены событием, а не рукой;
- `v0.1.0-alpha.18` проходит проверку релиза начисто — первый релиз, установка которого на чистый хост доказана автоматически.

Цепочка alpha.15 → alpha.18 за 73 минуты со стороны выглядит суетой, но ею не является: alpha.16 и alpha.17 упали на проверке по настоящим причинам — машиночитаемый вывод установщика и режим отделённой подписи cosign v3 — и каждый следующий релиз их устранял. Механизм отработал ровно так, как задумывался.

## Решение владельца от 2026-08-17

Установка переделывается: один файл с выбором роли Hub/узел, а код регистрации выдаёт приложение оператора — Windows-клиент или веб-консоль, из которых и так ведётся наблюдение. Ssh на Hub под root для выпуска кода уходит из обихода.

Работы больше, чем кажется по формулировке: `create_node_code` выполняется на Hub от root, дёргает локальный `control-cli token-create`, читает CA и `mesh.env`, резервирует адрес в подсети. HTTP-эндпоинта, создающего код, в Control нет — есть только приём готового токена (`POST /api/v1/enroll`). Кнопка в интерфейсе требует серверной части.

## Правила координации

- Codex владеет `deploy/**`, релизным пайплайном и проверкой релиза. Antigravity — стороной Desktop. Hermes недоступен; его область перешла к Codex.
- `linux-release.yml` остаётся единственным публикатором релизов.
- Опубликованный тег не двигается и ассеты не заменяются; исправление выпускается следующим тегом.
- Независимому review передаётся полный текст задания, а не только диф. На четырёх блоках подряд diff-only review пропускал целые невыполненные требования.
- Описание PR заполняется после завершения CI, со ссылками на конкретные прогоны.
- Отсутствие проверки — не дефект отчёта. Утверждение о проверке, противоречащее статусу CI, — дефект.
- Критерий приёмки, недостижимый в принципе, — дефект задания, а не исполнителя. «`Release Verification` отработал автоматически» в задании на `alpha.14` был таким: событие от `GITHUB_TOKEN` не могло его запустить.
- Горизонт 1 не начинается, пока не закрыт Горизонт 0, включая физическую приёмку.

## Архив

Завершённые задания и промежуточные review-пакеты — в `Старые задачи`. Текущим планом не являются.
