Задания, отчёты и патчи, лежавшие в C:\Users\Ochenstarik\projects и в домашней папке, перенесены в agents/. Разложено по агентам там, где имя файла позволяло определить автора; остальное — в _salvage-2026-08-18/ и разбирается вручную. Патчи в notes/salvage-2026-08-18/ — незакоммиченная работа из брошенных рабочих копий: она существовала только на диске. Тяжёлое (релизные архивы, инсталляторы, наборы данных) в репозиторий не попало: оно лежит рядом, в Agent_projects/_archive и Agent_projects/_data. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 KiB
Путь регистрации Node не работает от начала до конца
Приоритет: блокирующий. Найдено при первой в истории проекта установке на живые серверы 2026-08-09. Все четыре дефекта лежат на пути, который CI не проходит ни разу.
Репозиторий
- https://github.com/ochenstarik-ui/server-monitor-manager
- База:
main@d645812(= тегv0.1.0-alpha.7, из которого собран проверявшийся релиз) - Ветка:
hermes/node-enrollment-fix
Контекст
Hub ставится и работает: Control активен, /healthz отвечает Healthy, WireGuard smm0 поднят, таблица inet ochenstarik_smm создана. Дальше не проходит ничего.
Дефект 1 — конфигурация Agent не читается в опубликованном бинарнике (BLOCKER)
Наблюдение
$ sudo -u ochenstarik-smm-agent env SMM_NodeId='INVALID_UPPER' \
/usr/local/lib/ochenstarik-server-monitor-manager/agent/ochenstarik-smm-agent
Unhandled exception. System.IO.FileNotFoundException:
Could not find file '/var/lib/ochenstarik-server-monitor-manager/agent/agent.pfx'
Заведомо невалидный SMM_NodeId не вызвал ошибку валидации из Program.cs. Значит configuration.Get<AgentOptions>() вернул объект с умолчаниями, а все переменные окружения проигнорированы.
Дефект не виден снаружи, потому что умолчания валидны: NodeId по умолчанию — имя машины, числовые настройки в допустимых диапазонах. Единственное свойство без валидного умолчания — EnrollTokenFile (null), поэтому Program.cs не заходит в ветку регистрации, падает в RunAsync и ищет несуществующий agent.pfx.
Причина
Публикация идёт с -p:PublishTrimmed=true. Рефлексивная привязка конфигурации теряет метаданные свойств, вырезанные линкером.
Правильное решение в проекте уже применено к Control:
// ControlOptions.cs
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ControlOptions))]
public ControlOptions() { }
В AgentOptions такого атрибута нет. Поэтому Control конфигурацию читает, а Agent — нет.
Что сделать
Не ограничиваться копированием атрибута. Предпочтительно — включить source-generated биндер, он trim-safe по построению и снимает целый класс таких отказов:
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
в ServerMonitorManager.Agent.csproj и ServerMonitorManager.Control.csproj. Атрибут DynamicDependency в ControlOptions после этого можно оставить как страховку, но он перестаёт быть единственной защитой.
Отдельно: при неудачной привязке Agent обязан отказываться стартовать, а не тихо использовать умолчания. Добавить явную проверку — если SMM_ControlUrl или SMM_NodeId присутствуют в окружении, но не попали в options, это фатальная ошибка конфигурации с внятным сообщением. Молчаливый откат на умолчания в компоненте, который держит mTLS-идентичность, недопустим.
Дефект 2 — validate_control_url не пропускает ни один адрес (BLOCKER)
Наблюдение
$ sudo ochenstarik-server-monitor-manager.sh node-code ai-agent
ERROR: Control URL must be an https URL without a path or credentials.
/etc/ochenstarik-server-monitor-manager/control-public-url содержал корректный https://host1885995-3.hostland.pro:7443.
Причина
[[ "$1" =~ ^https://[A-Za-z0-9._:\[\]-]+(:[0-9]{1,5})?/?$ ]]
Внутри скобочного выражения POSIX обратный слэш не экранирует. Класс закрывается на первом ], а хвост -] становится обязательным литералом после хоста. Проверено: не совпадает даже https://example.com.
Что сделать
[[ "$1" =~ ^https://[]A-Za-z0-9._:[-]+(:[0-9]{1,5})?/?$ ]]
] первым символом класса, [ и - — последними. Проверено: принимает https://host:7443, https://example.com, https://10.0.0.1:7443; отклоняет https://a.b/path и http://x.y.
Функция вызывается в create_node_code и в install_agent — то есть дефект блокирует оба пути регистрации.
Обязательно: юнит-тест на validate_control_url в tests/bootstrap/test-bootstrap-contract.sh с набором принимаемых и отклоняемых адресов. Аналогично проверить остальные regexp с классами символов в bootstrap и helper — эта ошибка воспроизводится копипастом.
Дефект 3 — control-device-code не существует
tests/acceptance/three-server-mesh.sh:117 вызывает:
device_code="$(hub_ssh "$INSTALLER_COMMAND control-device-code '$CONTROL_DEVICE_ID'" …)"
В bootstrap девятнадцать действий, control-device-code среди них нет. В docs/installer-contract.md §5 оно значится как целевое, в Control есть CLI device-token-create — связки между ними никто не написал.
Приёмка падает на шаге 2 из 12, до всего остального.
Что сделать: добавить действие control-device-code DEVICE_ID, выдающее код SMMDEV1-... по образцу node-code, через существующий run_control_cli device-token-create.
Дефект 4 — INSTALLER_COMMAND указывает в пустоту
INSTALLER_COMMAND="${INSTALLER_COMMAND:-sudo /usr/local/sbin/ochenstarik-server-monitor-manager.sh}"
Bootstrap кладёт в /usr/local/sbin/ только ochenstarik-smm-emergency, себя — нет.
Что сделать: устанавливать bootstrap в /usr/local/sbin/ochenstarik-server-monitor-manager.sh при install-control и install-agent — это же соответствует docs/installer-contract.md §5, где CLI описан как системная команда. Либо изменить умолчание в скрипте приёмки. Первое лучше: документация уже обещает системную команду.
Дефект 5 — CI не проверяет путь регистрации
Первые два дефекта — блокирующие, лежали в репозитории неделями и пережили сотню зелёных прогонов. Причина одна: CI ни разу не запускает Agent из опубликованного релизного бинарника.
linux-control-agent.yml ставит только Control и дёргает /healthz. linux-platform-matrix.yml — то же самое. Публикация Agent выполняется, но получившийся бинарник только складывается в артефакт и никогда не исполняется.
Что сделать: расширить systemd-smoke до полного цикла на одной машине:
install-control;node-code— проверяет дефект 2;install-agentс полученным кодом — проверяет дефект 1, запускает опубликованный trimmed бинарник;- проверить, что сертификат Agent выпущен и
ochenstarik-smm-agent.serviceактивен; control-device-code— проверяет дефект 3.
Hub и Node на одной машине для этого допустимы: WireGuard не нужен, mesh не поднимается, проверяется только регистрация. Полноценный mesh по-прежнему требует физической приёмки.
Без этого шага любой из четырёх дефектов вернётся незамеченным.
Критерий приёмки
SMM_NodeId='INVALID_UPPER'на опубликованном trimmed бинарнике даёт ошибку валидации, а не падение наagent.pfx;install-nodeпо кодуSMMNODE2доводит Node до активногоochenstarik-smm-agent.serviceи выданного сертификата;validate_control_urlпринимает и отклоняет адреса по тесту в контрактном скрипте;control-device-codeсуществует и выдаётSMMDEV1-...;- bootstrap доступен как
/usr/local/sbin/ochenstarik-server-monitor-manager.sh; - CI выполняет полный цикл регистрации на опубликованных бинарниках;
- Control suite прогнан на Linux;
- PR создан, CI зелёный.
После merge
Выпустить v0.1.0-alpha.8 и повторить установку на живых серверах. Hub переустанавливать не нужно — он работает; достаточно update-control и заново пройти регистрацию Node.
Временный обход, применённый вручную
На Hub и в установщике smm-setup.sh дефект 2 обойдён заплаткой:
perl -pi -e 's/\Q[A-Za-z0-9._:\[\]-]\E/[]A-Za-z0-9._:[-]/' <bootstrap>
После merge обход из smm-setup.sh убрать — он помечен комментарием в коде.