Задания, отчёты и патчи, лежавшие в 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>
25 KiB
План доделывания Server Monitor Manager
Для исполнителя (Codex). Составлен 2026-08-04 против main @ b11c277 плюс незакоммиченная ветка hermes/task3-b3-fact-reconciliation.
Нормативные документы в репозитории после коммита 4de849e: docs/product-horizons.md, docs/approval-policies.md, docs/integration-kagent.md, обновлённые docs/roadmap.md и docs/security-model.md.
0. Правила работы
Проверены на четырёх блоках, менять не нужно.
- Один PR — одна тема. Смешанные PR не проходят review.
- Независимому review передаётся полный текст задачи, а не только диф. Три из четырёх блоков дали находки класса «пропущенное требование», которые при diff-only ревью не ищутся вовсе.
SHA256SUMSсчитается последним действием, после всех правок отчётов, и включает все файлы пакета.- Статус после merge —
merged / verified, а не «выполнено», если хотя бы один критерий сформулирован через фактическое поведение и не измерялся на реальной топологии. - Гейты горизонтов обязательны. Задача следующего горизонта не начинается, пока не закрыт предыдущий, даже если она кажется мелкой.
- Формат пакета сдачи:
REPORT.md,TEST_EVIDENCE.md, патч и/или ZIP,CI_CHECKS.txt,INDEPENDENT_REVIEW.md,SHA256SUMS.
Размеры: S — до дня, M — 2–4 дня, L — неделя и больше.
Горизонт 0 — закрыть начатое
Ничто из горизонтов 1–3 не начинается до полного закрытия этого раздела.
T0.1 — завершить B-3R · M · в работе
Ветка hermes/task3-b3-fact-reconciliation. Контракт: B3R_SPEC.md, уже принятый исполнителем.
Осталось по состоянию на 04.08: пакетная финальная верификация (1 + k вызовов link-list вместо двух), класс Deferred с инвариантом Converged + Failed + Deferred == Examined, R4 (source-generated DTO для orphan audit), R5 (Linux integration test на fact-first протокол), R6 (убрать fail-open default в ILinkPolicyApplier), R7 (форматирование вложенных scope).
Дополнительно, найдено при проверке: любое не-active значение статуса в nodes.tsv сейчас даёт exit 80 и уходит в Deferred навсегда. Мусор в поле статуса должен давать 78 — это повреждённое состояние, а не ожидаемое.
Готово, когда: PR создан, Linux CI зелёный впервые для этой ветки, merge выполнен.
T0.2 — физический acceptance · S · внешний блокер
SMM_ACCEPT_RESTORE=1 SMM_ACCEPT_REBOOT=1 tests/acceptance/three-server-mesh.sh
Требуются HUB_SSH_HOST, HUB_SSH_USER, SOURCE_SSH_HOST, SOURCE_SSH_USER, HOME_WG_IP, SECOND_WG_IP, SSH_IDENTITY_FILE. Harness готов и содержит шаги firewall-restore без перезапуска Node и инъекции orphan-правила.
Исполнитель закрыть не может — нужна выдача доступов. Висит четвёртый блок подряд и блокирует критерий B6, Этап 3 и Этап 5 roadmap.
Готово, когда: прогон завершился THREE_SERVER_ACCEPTANCE=PASS, вывод приложен к пакету.
T0.3 — роль Monitor в bootstrap · M
Сегодня SSH-мониторинг — функция, ради которой существует Desktop, — не имеет серверной части. В deploy/ochenstarik-server-monitor-manager.sh нет действия install-monitor, forced-command скрипт не поставляется, а парсер SshMonitorService.QueryAsync ожидает ключи (PROTOCOL, CPU_COUNT, MEM_AVAILABLE_KB, DISK_INODES_TOTAL…), которые в репозитории никто не производит.
Объём:
install-monitor PUBLIC_KEYиuninstall-monitor;- системный пользователь
ochenstarik-monitor: nologin, без пароля, без sudo; /usr/local/libexec/ochenstarik-smm-metrics, root-owned0755, выдающий ровно снимок изdocs/installer-contract.md§7 и read-onlymesh status;authorized_keysсcommand="…",restrict,no-pty,no-agent-forwarding,no-port-forwarding,no-X11-forwarding;- идемпотентность повторного запуска;
- контрактный тест формата снимка, общий для скрипта и парсера Desktop, чтобы они не разъезжались.
Готово, когда: чистый сервер попадает под мониторинг одной командой, README-инструкция соответствует коду, тест формата в CI.
T0.4 — подписанная поставка · M
Сейчас bootstrap сверяет ARCHIVE.sha256, лежащий рядом в том же релизе, а manifest не подписан и содержит только хэш самого bootstrap. Кто может опубликовать релиз — ставит произвольный root-код на весь парк через update-agent.
Объём:
- manifest v2: хэши всех артефактов, версии Control/Agent/helper/Desktop, минимальные совместимые версии,
helper_protocol; - подпись
cosign sign-blobkeyless либоminisign; публичный ключ или issuer вшит константой в bootstrap и Desktop, не скачивается вместе с релизом; verify-manifestкак отдельное действие;verify_archiveсверяет хэш с manifest, а не с соседним файлом; старый путь только под явнымSMM_ALLOW_UNSIGNED=1с предупреждением;- отказ
update-control/update-agentпри несовместимой паре версий; PROGRAM_VERSIONподставляется из тега при упаковке вместо0.2.0-dev;- негативные тесты в CI: изменённый байт архива, подменённый хэш в manifest, manifest без подписи — все три отвергаются.
Готово, когда: установка и обновление возможны только из подписанного релиза, три негативных теста зелёные.
T0.5 — жизненный цикл сертификатов · S
CertificateAuthority.cs:55 выдаёт клиентские сертификаты на год, автопродления нет. Через год весь парк отваливается одновременно.
- сократить срок до 30–90 дней;
- автопродление Agent за треть срока до истечения, через существующий mTLS-канал;
- событие
certificate.expiringв поток событий; - документированная процедура ротации Control CA и повторной регистрации парка.
T0.6 — гигиена репозитория и цепочки поставки · S
- пиннинг всех GitHub Actions по commit SHA (сейчас плавающие
@v6,@v5,@v2); .github/dependabot.ymlдляgithub-actionsиnuget;- сужение
permissions: contents: writeдо job'а публикации; SECURITY.md— обязателен для инструмента безопасности;CHANGELOG.md,CONTRIBUTING.md,CODEOWNERS, шаблоны issue/PR;- SBOM (
dotnet CycloneDX) в артефакты релиза; - удалить слитые ветки
agent/remove-lightweight-server-references,codex/ttl-backup-acceptance.
T0.7 — базовая инженерная оснастка · S
Делается один раз и удешевляет всё последующее.
Directory.Build.props:Nullable,TreatWarningsAsErrors,EnableNETAnalyzers,AnalysisLevel latest-recommended, детерминированная сборка;Directory.Packages.props(central package management) иRestorePackagesWithLockFile— сейчас сборки невоспроизводимы, что противоречит требованию закреплённой поставки;- разделить тесты:
Core.Tests,Control.Tests,Agent.Tests,Helper.Tests. СейчасLinuxMetricsTests,MetricBufferTestsиProvisioningHelperTestsлежат в проекте Control; - тесты round-trip сериализации контрактов — при
PublishTrimmed=trueмолчаливая потеря поля ничем не ловится.
Гейт Горизонта 0. Чистый сервер ставится из подписанного релиза одной командой, регистрируется одноразовым кодом, попадает под мониторинг без ручной правки authorized_keys, и после перезагрузки Hub и Node фактическое состояние Links совпадает с желаемым — измерено связностью на реальной топологии.
Горизонт 1 — продукт для одного владельца
T1.1 — каркас provisioning-модулей · M · делать первым
Сейчас вся логика мутации живёт в TimezoneProvisioningExecutor одним классом на 320+ строк, а IsTimezoneOnly жёстко фиксирует VmSwappiness == 60, пустые пакеты и RebootPolicy = never. Второй модуль скопирует backup/verify/rollback целиком.
IProvisioningModule
string ActionType { get; }
string ModuleHash { get; }
ValidationResult Validate(JsonElement parameters);
Plan BuildPlan(JsonElement parameters); // без мутаций
ExecutionResult Execute(Plan plan, Grant grant); // backup → mutate → verify → rollback
FactualState Observe();
Перенести timezone на каркас без изменения поведения, покрыть тем же набором тестов. Добавление модуля после этого — один файл, одна схема, один тест.
Готово, когда: timezone работает через каркас, существующие тесты зелёные без правок логики.
T1.2 — provisioning-модули по одному · L
Порядок по возрастанию риска: system.locale → system.packages (versioned allowlist уже есть) → system.swap → user.create → user.disable → ssh.key.add → ssh.key.remove.
Каждый модуль: versioned JSON schema, immutable plan, подтверждение по политике, factual verification, rollback, audit, тесты, прогон на VM-матрице. Новый пользователь по умолчанию без sudo. Приватные ключи на Hub не попадают.
T1.3 — политики подтверждения · M
По docs/approval-policies.md. Девять режимов вместо бинарного подтверждения. Решение принимает Control, не клиент. Понижение режима для конкретного задания невозможно, изменение политики — аудируемое событие.
Зависимость: T1.1, потому что режим выбирается по ActionType.
T1.4 — firewall и двухфазная миграция SSH · L · самая опасная задача проекта
По docs/provisioning-vpn-requirements.md §10. Порядок шагов там уже выписан и его надо соблюсти буквально.
Критическое требование: порт 22 невозможно закрыть до успешной второй SSH-сессии на новом порту. Отдельное подтверждение на закрытие старого порта. При любой ошибке — откат drop-in и правила firewall с сохранением активной сессии.
Обязательный тест: новый порт не поднялся → 22 остался открыт и доступен.
Делать только после T1.1–T1.3 и полного Горизонта 0.
T1.5 — alert engine · M
Пороги, warning/critical, duration, cooldown, дедупликация, режим обслуживания, подтверждение, эскалация, история инцидентов, автозакрытие, тихие часы. Формат правила уже согласован:
name: Disk almost full
metric: disk.used_percent
condition: "> 90"
duration: 10m
severity: critical
channels: [telegram, email]
cooldown: 30m
T1.6 — Telegram, только чтение · S
/status, /servers, /alerts, /links, /jobs. Никаких мутаций, приватных ключей, управления CA и произвольных команд. Токен — секрет; до появления Vault хранить как остальные секреты Control, с маскированием в диагностике и аудите.
T1.7 — наблюдение Docker и systemd · M
Сначала только чтение: контейнеры, образы, healthcheck, счётчики рестартов, отсутствующие лимиты, failed units, journald. Действия — отдельной задачей, только по allowlist, с подтверждением и аудитом, без произвольных аргументов.
T1.8 — Backup Manager · M
Источники: каталоги, конфигурации, Docker volumes, Control DB и CA. Хранилища local и S3. Расписания, шифрование, retention, контрольные суммы, проверка архива, тестовое восстановление, оповещения об ошибке и об устаревшей копии.
T1.9 — технический долг Control и Desktop · M · фоном
ControlStore.cs1576 строк: разделить наControlSchema,IdentityStore,MetricStore,LinkStore,AuditStore,IdempotencyStore;MainPage.xaml.cs1124 строки без MVVM и DI: вынести ViewModel, добавить unit-тесты Desktop;- локализация: русские строки захардкожены в бизнес-логике (
SshMonitorService,ControlClientService); ввести.resw, в исключениях — коды, текст на уровне UI; HttpClientсоздаётся на каждый запрос вControlClientServiceиAgentClient: одинSocketsHttpHandlerсPooledConnectionLifetime;- поток событий: durable log,
GET /events?since=<sequence>, keepalive раз в 15 с, лимит подписок, экспоненциальный backoff с джиттером на клиенте; - rate limiting не только на enrollment: политики на группы
agentsиcontrol,MaxConcurrentConnections; - единый источник валидаторов:
NodeIdValidatorи прочие живут вProgram.csи продублированы в bash и helper — три реализации одних правил.
Гейт Горизонта 1. Каждый мутирующий модуль имеет схему, immutable plan, подтверждение, factual verification, rollback и audit и прошёл физическую приёмку на VM-матрице.
Горизонт 2 — платформа
T2.1 — решение по публичному интерфейсу · S · до всякого кода
Заполнить раздел «Публичный интерфейс» в docs/security-model.md: отдельный listener и порт, отношение веб-сессий к mTLS-идентичностям, перечень операций, недоступных веб-сессии в принципе, привязка сессии к устройству, CSRF, rate limiting, повторная аутентификация.
Без этого решения Web UI не начинается.
T2.2 — Secret Vault · M
Envelope encryption, версии, ротация, маскированный показ, scoped delivery, TTL, отзыв, redaction. Предусловие для Telegram-токена, ключей S3 и VPN-подписок.
T2.3 — Web UI и аутентификация · L
Разделы: Dashboard, Servers, Metrics, Links, Provisioning, Alerts, Audit, Users, Automation identities, Backups, Settings. Аутентификация: пароль, TOTP, коды восстановления, WebAuthn, passkeys, доверенные устройства, сессии, RBAC, повторная аутентификация.
Операции класса firewall / CA / удаление Node / экспорт секретов — только по mTLS-идентичности, не по веб-сессии.
T2.4 — решение по хранилищу логов · S · до кода
Логи journald + Docker + nftables + SSH со всех узлов не лягут на Control SQLite и раздуют резервные копии. Выбрать: отдельное хранилище, выгрузка вовне, либо только tail на узле без центрального retention.
T2.5 — централизованные логи · L
После T2.4. Поиск, фильтры, уровни, retention, redaction, таймлайн инцидента, экспорт диагностики, корреляция.
T2.6 — security posture · M
Оценка, объяснения, план исправления, безопасное применение, история, исключения, принятые риски, отчёты.
T2.7 — multi-Hub, фаза 1 · M
Резервные копии БД и CA, шифрованное хранилище, проверка восстановления, ручное переключение. Репликация, active/passive и кворум — вне плана.
T2.8 — один VPN-модуль · L
Xray VLESS Reality, полный жизненный цикл: preflight, установка, обновление, отключение, verification, rollback timer, routing exclusions, kill switch с management exceptions, аварийное отключение без Control, reboot-тест, reconciliation, ротация секретов.
Второй VPN-модуль не начинается, пока первый не прошёл физическую приёмку. Шесть наполовину сделанных VPN — это шесть способов потерять доступ к серверу.
Горизонт 3 — интеграция с KAgent
По docs/integration-kagent.md. Строго в порядке: T3.1 → T3.4.
T3.1 — обнаружение и идентичность · M
Unix-сокет /run/server-monitor-manager/integration.sock, root:smm-integrations, 0640, каталог 0750. Обязательно переиспользовать меры из ProvisioningHelperServer: SO_PEERCRED со сверкой uid, обработка вне цикла accept, ограничение параллелизма, таймаут соединения, буферное чтение, лимиты частоты. Роль KAgentIntegration с одноразовым token, сроком, областью Nodes и отзывом.
T3.2 — чтение · M
Nodes, метрики, здоровье, события. Согласование версии протокола и фактически выданных возможностей. Невыдаваемые возможности отсутствуют как код, а не выключены настройкой.
T3.3 — планы и запросы · L
*.plan и *.request порождают обычный typed provisioning job с immutable plan, режимом подтверждения, execution grant, factual verification и audit. Отдельного пути для AI нет. *.request возвращает идентификатор задания, а не результат.
T3.4 — жизненный цикл Worker · L · наивысший риск в проекте
Инвариант недоверенного исполнителя из docs/security-model.md — главное требование, всё остальное вторично. Отдельный пользователь без sudo, контейнер с read-only rootfs, seccomp, AppArmor, сброшенными capabilities; нет доступа к сокету helper, к agent.pfx, control-ca.crt, nodes.tsv, каталогу rollback; нет сетевого доступа к Control API; порт наружу не публикуется.
Задачные Links: обязательный TTL, запрет destination = Hub или другой Worker, снятие по завершении, отказу, таймауту, offline, истечению аренды и аварийной остановке, с фактической проверкой снятия.
Аварийные средства: отзыв сертификата, остановка всех Worker, отключение задачных Links, блокировка новых запросов, режим только чтения, карантин, отключение сети, сохранение журналов.
Что не делать
- Не начинать VPN-модули до закрытия Горизонта 1. Kill switch при незакрытом расхождении desired/factual — гарантированная потеря доступа к серверу.
- Не добавлять второй provisioning-модуль копированием timezone-исполнителя. Сначала T1.1.
- Не проектировать API KAgent против сущностей, которых нет:
containers,services,logs,backupsпоявляются вместе с самими подсистемами. - Не начинать macOS/Linux Desktop и мобильные клиенты до стабилизации контрактов
Core— они меняются почти каждым PR. - Не делать репликацию и кворум multi-Hub.
- Не расширять расширенный мониторинг (SMART, RAID, GPU, UPS) — каждый пункт требует привилегированного сборщика и отдельной модели угроз.
Критический путь
T0.1 ─┐
T0.3 ─┼─► T0.2 (внешний блокер) ─► гейт Г0 ─► T1.1 ─► T1.2 ─► T1.3 ─► T1.4 ─► гейт Г1 ─► Горизонт 2 ─► Горизонт 3
T0.4 ─┤
T0.5 ─┘
T0.6 и T0.7 идут параллельно и ничего не блокируют. T1.5–T1.9 параллельны T1.2–T1.4.
Единственное, что нельзя обойти работой исполнителя, — T0.2. Он дешевле любой другой задачи в этом плане и блокирует всё.