server-monitor-manager/agents/codex/inbox/from-smm-deliverables/Старые задачи/smm-codex-plan-2026-08-04.md
Ochenstarik 23eb3f5233 chore(agents): разбор рабочих папок с диска на 2026-08-18
Задания, отчёты и патчи, лежавшие в 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>
2026-08-18 14:19:54 +07:00

25 KiB
Raw Permalink Blame History

План доделывания 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. Правила работы

Проверены на четырёх блоках, менять не нужно.

  1. Один PR — одна тема. Смешанные PR не проходят review.
  2. Независимому review передаётся полный текст задачи, а не только диф. Три из четырёх блоков дали находки класса «пропущенное требование», которые при diff-only ревью не ищутся вовсе.
  3. SHA256SUMS считается последним действием, после всех правок отчётов, и включает все файлы пакета.
  4. Статус после merge — merged / verified, а не «выполнено», если хотя бы один критерий сформулирован через фактическое поведение и не измерялся на реальной топологии.
  5. Гейты горизонтов обязательны. Задача следующего горизонта не начинается, пока не закрыт предыдущий, даже если она кажется мелкой.
  6. Формат пакета сдачи: REPORT.md, TEST_EVIDENCE.md, патч и/или ZIP, CI_CHECKS.txt, INDEPENDENT_REVIEW.md, SHA256SUMS.

Размеры: S — до дня, M — 24 дня, L — неделя и больше.


Горизонт 0 — закрыть начатое

Ничто из горизонтов 13 не начинается до полного закрытия этого раздела.

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-owned 0755, выдающий ровно снимок из docs/installer-contract.md §7 и read-only mesh 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-blob keyless либо 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 выдаёт клиентские сертификаты на год, автопродления нет. Через год весь парк отваливается одновременно.

  • сократить срок до 3090 дней;
  • автопродление 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.localesystem.packages (versioned allowlist уже есть) → system.swapuser.createuser.disablessh.key.addssh.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.1T1.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.cs 1576 строк: разделить на ControlSchema, IdentityStore, MetricStore, LinkStore, AuditStore, IdempotencyStore;
  • MainPage.xaml.cs 1124 строки без 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.5T1.9 параллельны T1.2T1.4.

Единственное, что нельзя обойти работой исполнителя, — T0.2. Он дешевле любой другой задачи в этом плане и блокирует всё.