# Codex: единый установщик с выбором роли ## Порядок Это задание выполняется **после** `smm-codex-task-alpha15-2026-08-15.md`. Пока не поставляется cosign, установка на чистый сервер невозможна, и переделывать интерфейс установщика поверх неработающей установки бессмысленно. ## Репозиторий - https://github.com/ochenstarik-ui/server-monitor-manager - База: `main` после слияния alpha.15 - Ветка: `codex/unified-installer` ## Правила репозитория - `main` защищён: PR обязателен, обязательны статусы `build-and-test` и `build`, force-push запрещён; - один PR — одна тема; - описание PR заполняется **после** завершения CI, со ссылками на конкретные прогоны; - `docs/release-policy.md` запрещает двигать опубликованные теги и заменять ассеты; - `.github/workflows/linux-release.yml` — единственный публикатор релизов. --- ## Зачем Живая установка на четырёх машинах показала, что оператор ошибается не в сложных местах, а в простых: путает роль, путает машину, оставляет плейсхолдер в аргументе, прерывает молчаливую загрузку. Владелец просит один файл, который спрашивает роль и делает остальное сам. Второе изменение: код регистрации узла больше не выпускается на Hub через ssh. Его выдаёт приложение оператора — Windows-клиент или веб-консоль. Соответствующий эндпоинт делает Antigravity заданием `smm-antigravity-task-web-console-2026-08-17.md`. Установщику от этого нужно одно: принимать готовый код `SMMNODE2...`, как он делает и сейчас. ## Что сделать Единый интерактивный режим `smm-setup.sh` без аргументов: 1. показать, что за машина: дистрибутив, версия, архитектура, публичный адрес, наличие уже установленных компонентов; 2. спросить роль — Hub или узел; 3. для Hub: спросить публичный адрес, предложив определённый автоматически как значение по умолчанию; порты предложить `7443` и `51820`; 4. для узла: попросить код `SMMNODE2...`, показать разобранный из кода адрес Hub и отпечаток CA, потребовать подтверждения; 5. проверить и доустановить зависимости, включая cosign; 6. скачать и проверить релизные файлы с видимым индикатором; 7. выполнить установку; 8. по завершении Hub напечатать **то, что нужно вставить в приложение оператора**: код регистрации устройства и отпечаток CA. Иначе получается замкнутый круг — приложение не может выдать код узла, пока само не подключено к Hub; 9. по завершении узла напечатать `SMMPEER1...` и прямо сказать, куда его вставить. Требования к поведению: - существующие неинтерактивные команды сохраняются полностью: они используются в `tests/acceptance/three-server-mesh.sh` и в проверке релиза. Интерактивный режим — это режим по умолчанию при запуске без аргументов, а не замена; - при отсутствии терминала (запуск из скрипта, `stdin` не tty) интерактивный режим не включается, а выдаётся подсказка с неинтерактивными командами; - ни один ввод не приводит к выполнению постороннего кода: код регистрации, адрес и порты проверяются до использования; - повторный запуск на уже установленной машине распознаёт это и предлагает осмысленный выбор, а не падает и не ставит поверх. ## Состояние на 2026-08-18 **Установочная часть выполнена и слита** — PR #58, `main` @ `f7b10ac`: интерактивный режим без аргументов, выбор роли Hub/Node, показ состояния машины, доустановка зависимостей, разбор кода `SMMNODE2`, отказ без tty, тесты в `tests/bootstrap/test-unified-installer.sh`, обновлённая документация. **Удаление не сделано.** В меню по-прежнему два пункта: `Choose role: 1) Hub 2) Node`, и `die "choose Hub (1) or Node (2)"` на всё остальное. Требование ниже появилось в задании 2026-08-18 и, судя по всему, до исполнителя не дошло. Оно остаётся в силе и выполняется **отдельным PR** поверх слитого. Релиз с единым установщиком **не выпускать до готовности удаления**: смысл в том, чтобы владелец получил одну программу, умеющую и поставить, и снять. Выпуск половины означает второй релиз следом, а проект уже сжигал теги на такой спешке. --- ## Дополнение от владельца 2026-08-18 — полное удаление в том же файле PR #58 закрывает установку, но не удаление. Владелец требует, чтобы единый файл умел и снимать систему целиком: третий пункт меню рядом с Hub и Node. Сегодня `uninstall-agent` и `uninstall-control` снимают не всё. После них остаются системные пользователи и группы, конфигурация WireGuard и юнит `wg-quick@smm0`, таблица nftables `inet ochenstarik_smm`, каталоги состояния, запись в `sudoers.d`, файл в `sysctl.d`. Владелец проходил это вручную; точный список — в `smm-cleanup-and-install-2026-08-15.md`, раздел 1, проверен на живых серверах. ### Что сделать Третий выбор роли: **удалить всё**. Поведение: 1. **Сначала показать, что будет удалено**, и только потом спрашивать подтверждение. Список строится по фактическому состоянию машины, а не печатается шаблоном: какие юниты найдены, какие пользователи существуют, есть ли таблица nftables, есть ли `smm0`, какие каталоги непусты. 2. **Подтверждение — набранным словом, не `y/N`.** Прецедент есть: `uninstall-control` требует `--confirm-destroy-control`. Случайное нажатие не должно сносить Hub вместе с его CA. 3. **Порядок: сначала остановить службы, потом удалять юниты.** Это не педантизм. Владелец однажды удалил юниты при работающем процессе, тот осиротел, продолжил держать порт 7443, и `userdel` отказал, потому что у пользователя оставался живой процесс. Правильно так: `systemctl disable --now`, затем `pkill -u` по каждому пользователю, пауза, `pkill -9 -u`, и только потом удаление файлов и учётных записей. 4. **Идемпотентность.** На машине, где ничего не стояло, режим отрабатывает молча и с нулевым кодом возврата. 5. **Ничего чужого.** На серверах владельца работает 3x-ui. Удаляются только объекты с собственными именами: юниты `ochenstarik-smm-*`, пользователи `ochenstarik-smm-control`, `ochenstarik-smm-agent`, `ochenstarik-monitor`, таблица `inet ochenstarik_smm`, интерфейс `smm0`, каталоги под своими путями. Никаких `rm -rf` по маскам, способным задеть соседа. 6. **Сохранение данных — отдельный вопрос.** Два варианта: снять программу, оставив резервные копии и базу Control, либо снести всё вместе с ними. Второй — с отдельным подтверждением: вместе с базой уходит CA, а с ним все выданные сертификаты и вся регистрация узлов. 7. **В конце — проверка**: напечатать те же четыре строки, которыми владелец проверяет чистоту вручную: свободны ли порты `7443` и `51820`, нет ли маршрутов `10.77.0.`, остались ли юниты, остались ли пользователи. Сквозной неинтерактивный вариант тоже нужен — для скриптов и повторной приёмки: команда с обязательным флагом подтверждения, без вопросов. ### Тесты удаления - на машине без установленной системы режим отрабатывает без ошибок и ничего не меняет; - после полной установки и последующего удаления не остаётся ни юнитов, ни пользователей, ни таблицы nftables, ни интерфейса, ни каталогов состояния; - посторонний юнит и посторонний пользователь, созданные до удаления, остаются нетронутыми; - без флага подтверждения неинтерактивный вариант отказывается работать; - удаление при работающих службах не оставляет процессов, удерживающих порты. ## Что проверить тестами - интерактивный сценарий Hub и узла целиком, на подставном вводе; - отказ интерактивного режима без tty; - отклонение испорченного кода `SMMNODE2` — обрезанного, с лишним сегментом, с подменённым отпечатком; - сохранность неинтерактивных команд: существующие тесты `tests/bootstrap/**` и проверка релиза продолжают проходить без изменений в вызовах. ## Границы Antigravity ведёт Control и веб-консоль. **Не трогать:** `src/**`, `.github/workflows/windows-build.yml`. Ваша область: `deploy/**`, `tests/bootstrap/**`, `docs/linux-bootstrap.md`. Формат `SMMNODE2` и `SMMPEER1` не менять: его разбирает установленный на серверах bootstrap, а опубликованные релизы неизменяемы. ## Критерий приёмки - `smm-setup.sh` без аргументов проводит установку Hub и узла целиком, включая cosign и зависимости; - Hub по завершении печатает то, что вставляется в приложение оператора; - неинтерактивные команды не изменены, приёмочный скрипт и проверка релиза проходят; - без tty интерактивный режим не запускается; - испорченный код отвергается; - всё через PR, CI зелёный, PR не смержен до зелёного. ## Отчёт Раздельно: что проверено локально, что в CI со ссылками на прогоны, что не проверялось и почему.