chore: актуализация проекта — приёмка, задания и история релизов в документации #65

Merged
ochenstarik-ui merged 11 commits from claude/deps-update into main 2026-08-20 10:48:03 +00:00
20 changed files with 1530 additions and 1 deletions

View file

@ -7,8 +7,198 @@ Versions follow the tags in this repository.
## [Unreleased]
### Added
- Link management and a real-time Event Journal in the Control web console (#63)
### Changed
- Task, report, and acceptance workflow for agents moved into `agents/` (#64)
- `actions/download-artifact` pinned to v8.0.1 across all workflows (#46)
- `docker/setup-qemu-action` pinned to v4.2.0 (#44)
---
## [v0.1.0-alpha.20] — 2026-08-18
Published. Carries the work that `v0.1.0-alpha.19` failed to publish.
### Added
- Release pipeline now tests the latest published release before cutting a new
one (#62)
- Operator web console for nodes, links, and enrollment code generation (#59)
- Guided unified installer (#58)
- Complete server uninstall mode (#60)
- Testing-only password login for the web console (#61)
- Node enrollment endpoint (#53)
- Desktop update proof (#55)
### Fixed
- Mesh state directory permissions created before Control starts (#57)
- `DEFAULT_RELEASE_TAG` in the packaged convenience installer matches the tag
being produced — the defect that burned `v0.1.0-alpha.19`
---
## [v0.1.0-alpha.19] — 2026-08-18 — burned, not published
The tag exists, but the Release pipeline failed before a GitHub Release was
published: the packaged convenience installer's `DEFAULT_RELEASE_TAG` did not
match the immutable tag. The version number is burned and must not be moved,
deleted, recreated, or reused. Its content was published under
`v0.1.0-alpha.20`.
---
## [v0.1.0-alpha.18] — 2026-08-18
### Fixed
- Wrong-identity negative test selects cosign v3 explicit detached-output mode,
matching the production consumer's detached signature and certificate
contract (#56)
First release required to complete both automatic `workflow_run` verification
and manual `workflow_dispatch` re-verification.
---
## [v0.1.0-alpha.17] — 2026-08-18
### Fixed
- Checksum verification stays fail-closed while its success line is suppressed,
so pass-through commands such as `node-code` return only machine-readable
bootstrap output (#54)
Automatic verification completed clean-host Hub and Node installation, then the
negative-test harness stopped while creating a wrong-identity signature:
cosign v3 requires an explicit bundle or legacy detached-output mode.
---
## [v0.1.0-alpha.16] — 2026-08-17
### Fixed
- Acceptance shell clears its command hash after removing test-provisioned
cosign (#52)
Automatic verification proved clean-host Hub installation, then failed on the
Node: the convenience installer wrote the checksum success line to stdout ahead
of the machine-readable `SMMNODE2` enrollment code, so the value was rejected.
---
## [v0.1.0-alpha.15] — 2026-08-17
### Added
- Pinned, checksum-verified cosign binary provisioned by the installer (#43)
First release that provisions cosign. Its verification proved clean-host Hub
installation and manifest verification, then stopped before the clean-host Node
installation: the acceptance script retained the deliberately removed cosign
path in Bash's command hash.
---
## [v0.1.0-alpha.14] — 2026-08-15
### Fixed
- Release signing consistency between producer and consumer (#41, #42)
First release with the complete manifest, keyless signature, and Fulcio
certificate set, so its published assets can be verified. A clean host cannot
install it because the release does not provision cosign — preserve it for
verification and historical evidence, do not use it for installation.
---
## [v0.1.0-alpha.13] — 2026-08-13
### Fixed
- Windows checksum asset normalized to LF so GNU `sha256sum -c` can consume it
(#39)
Published with a keyless manifest signature but without the Fulcio signing
certificate required by production consumers. The immutable release remains
published as historical evidence.
---
## [v0.1.0-alpha.12] — 2026-08-13
### Fixed
- Network-dependent alpha.8 compatibility test replaced in release
verification (#38)
Published with a keyless manifest signature but without the Fulcio signing
certificate, so consumers cannot verify that signature. Its Windows
`SHA256SUMS` asset also used CRLF and was not consumable by GNU
`sha256sum -c`. Neither defect is repaired in place.
---
## [v0.1.0-alpha.11] — 2026-08-12 — burned, not published
The tag exists, but the Release pipeline failed before a GitHub Release was
published. The version number is burned and must not be moved, deleted,
recreated, or reused.
Content carried by the tag: release contract updated to alpha.10, backward
compatibility test removed from the bootstrap job, Debian VM reboot constraint
documented, translations and roadmap synchronized.
---
## [v0.1.0-alpha.10] — 2026-08-11 — burned, not published
The tag exists, but the Release pipeline failed before a GitHub Release was
published. The version number is burned and must not be moved, deleted,
recreated, or reused.
Content carried by the tag: signed delivery queue B (#35), version comparison
fix (#36).
---
## [v0.1.0-alpha.9] — 2026-08-10
### Added
- Monitor snapshot contract (#32)
### Changed
- Single-writer release pipeline: `linux-release.yml` is the sole GitHub
Release publisher (#33)
`smm-setup.sh`, its checksum, the bootstrap script, platform archives, SBOMs,
and the signed manifest are reproducible from the tagged tree. The installer
fetches only same-tag assets and verifies the bootstrap checksum before
execution.
---
## [v0.1.0-alpha.8] — 2026-08-10
### Added
- Source-scoped monitor role (#29)
- Certificate lifecycle management
- Signed delivery, queue A
- Reproducible builds (#15)
- Product horizons and KAgent integration specifications (#13)
### Fixed
- CycloneDX SBOM generation in release jobs (#28)
- Repository hygiene (#14)
Contains the v1 `server-monitor-manager-bootstrap-manifest.json` layout and an
orphaned `server-monitor-manager-manifest.sig` without the corresponding
manifest v2. Published assets remain immutable; the anomaly is documented
rather than repaired in place.
---
## [v0.1.0-alpha.7] — 2026-08-09
### Added
- Background link reconciliation service for the Control plane (#12)
- Link reconciliation driven by factual state (#16)
- Provisioning helper coverage for a missing node row (#17)
### Changed
- Link policy reconciliation now runs continuously (#11)
@ -90,7 +280,21 @@ Versions follow the tags in this repository.
- Secure node enrollment documentation
- Windows build verification in CI
[Unreleased]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.6...HEAD
[Unreleased]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.20...HEAD
[v0.1.0-alpha.20]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.19...v0.1.0-alpha.20
[v0.1.0-alpha.19]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.18...v0.1.0-alpha.19
[v0.1.0-alpha.18]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.17...v0.1.0-alpha.18
[v0.1.0-alpha.17]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.16...v0.1.0-alpha.17
[v0.1.0-alpha.16]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.15...v0.1.0-alpha.16
[v0.1.0-alpha.15]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.14...v0.1.0-alpha.15
[v0.1.0-alpha.14]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.13...v0.1.0-alpha.14
[v0.1.0-alpha.13]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.12...v0.1.0-alpha.13
[v0.1.0-alpha.12]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.11...v0.1.0-alpha.12
[v0.1.0-alpha.11]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.10...v0.1.0-alpha.11
[v0.1.0-alpha.10]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.9...v0.1.0-alpha.10
[v0.1.0-alpha.9]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.8...v0.1.0-alpha.9
[v0.1.0-alpha.8]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.7...v0.1.0-alpha.8
[v0.1.0-alpha.7]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.6...v0.1.0-alpha.7
[v0.1.0-alpha.6]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.5...v0.1.0-alpha.6
[v0.1.0-alpha.5]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.4...v0.1.0-alpha.5
[v0.1.0-alpha.4]: https://github.com/ochenstarik-ui/server-monitor-manager/compare/v0.1.0-alpha.3...v0.1.0-alpha.4

View file

@ -0,0 +1,44 @@
# Приёмка: управление Links и журнал событий в веб-консоли
- **Исполнитель:** `antigravity`
- **Принял:** `claude` (главный агент)
- **Дата приёмки:** 2026-08-19
- **PR:** #63, слит коммитом `6115ed0` в `main`
## Состав пары
| Файл | Что это |
|------|---------|
| `task.md` | задание, было `agents/antigravity/inbox/from-smm-deliverables/smm-antigravity-task-console-links-2026-08-18.md` |
| `review-by-claude.md` | разбор главного агента, был `agents/claude/done/2026-08-18-review-pr63-console-links.md` |
Отдельного файла отчёта исполнителя нет: отчёт оформлен разделом в описании
PR #63, как принято в проекте.
## Основание приёмки
Диф снят от базы слияния `7c43977`. Границы задания соблюдены: 4 файла,
`deploy/**`, релизные workflow и bootstrap-тесты не затронуты, новых
эндпоинтов Control нет. Дефектов безопасности не найдено: экранирование в
консоли сплошное, разбор NDJSON держит границу чанка, журнал ограничен,
переподключение не размножает таймеры. Подробности — в `review-by-claude.md`.
Перед слиянием ветка обновлена относительно `main` (коммит `2459660`),
проверки `build` и `build-and-test` пройдены на обновлённой базе.
## Принято с открытыми замечаниями
Два замечания разбора в код не вносились и слияние не блокировали. По ним
выпущены отдельные задания:
- `agents/antigravity/inbox/2026-08-19-console-behaviour-tests.md`
тесты проверяют строки в статическом HTML, а не поведение;
- `agents/antigravity/inbox/2026-08-19-console-events-throttle.md`
обновление дашборда по событиям не троттлится.
## Чего приёмка не доказывает
`dotnet build` и `dotnet test` главным агентом не запускались: на машине
приёмки нет .NET SDK. Заявленные исполнителем 158 пройденных тестов и
зелёные прогоны CI приняты по ссылкам на прогоны. Консоль в браузере не
открывалась, поведение вручную не проверялось.

View file

@ -0,0 +1,116 @@
# Отчёт: разбор PR #63 «Link management и Event Journal в веб-консоли»
- **Задание:** роль главного агента, подтверждена владельцем в чате 2026-08-18
- **Агент:** `claude` (в роли главного агента)
- **Дата:** 2026-08-18
- **Разбираемая работа:** PR #63, ветка `antigravity/console-links`,
коммит `051d917`; задание —
`agents/antigravity/inbox/from-smm-deliverables/smm-antigravity-task-console-links-2026-08-18.md`;
отчёт исполнителя — описание PR #63.
## Что сделано
Разбор по репозиторию, а не по отчёту: диф снят от базы слияния
`7c43977`, а не от `main`.
## Как проверено
```
$ git merge-base origin/main origin/antigravity/console-links
7c43977fd137c2236f7fc100f5d972f4e5f2bcac
$ git diff --stat 7c43977 origin/antigravity/console-links
src/ServerMonitorManager.Control/wwwroot/app.js | 722 +++++++++++++++++----
src/ServerMonitorManager.Control/wwwroot/index.html| 179 ++++-
src/ServerMonitorManager.Control/wwwroot/style.css | 156 ++++-
tests/.../WebConsoleTests.cs | 45 +-
4 files changed, 976 insertions(+), 126 deletions(-)
```
Границы задания соблюдены: `deploy/**`, релизные workflow и bootstrap-тесты
не затронуты, новых эндпоинтов нет. Заявленный в описании PR состав файлов
совпадает с фактическим.
**Проверено и подтверждается:**
- **XSS не найден.** `escapeHtml` (app.js:1045) экранирует пять сущностей
(`& < > " '`) и применён при каждой интерполяции в `innerHTML`, включая
payload событий (app.js:479), `lastError` (338) и `data-*`-атрибуты
(347351). Непроэкранированных вставок пользовательских данных не найдено.
- **Разбор NDJSON корректен.** app.js:412413 — `split('\n')` с
возвратом хвоста в буфер через `pop()`; разрыв события на границе чанка
не теряется и не портит разбор.
- **Переподключение не размножается.** `startEventStream` вызывается ровно
из двух мест (app.js:89 при инициализации и 431 по таймеру), в начале
функции прежний `AbortController` прерывается. Накопления параллельных
таймеров нет.
- **Журнал ограничен**: `MAX_EVENTS_IN_JOURNAL = 100`, `unshift` + `pop`
(app.js:437439). Неограниченного роста в долгоживущей вкладке нет.
## Замечания
**1. Тесты не проверяют поведение — только строки в статическом HTML.**
Оба теста (`WebConsoleHtmlContainsRequiredUiElementsAndWarning` и новый
`WebConsoleProvidesLinkManagementAndEventsJournal`) состоят из
`Assert.Contains("id=\"…\"", html)` по разметке. Они доказывают, что в
`index.html` есть элементы с нужными идентификаторами, и ничего больше.
При этом 976 изменённых строк — почти целиком поведение на JavaScript:
создание Link, отключение, идемпотентность, разбор потока событий,
расчёт срока действия, разбор Problem Details. Ни одна из этих веток
тестом не покрыта.
В описании PR «158 пройденных тестов» приведено как доказательство работы
управления Links. Это не доказательство: пройдёт и разметка с правильными
`id` при полностью нерабочем `app.js`. Соглашение 6 из
`agents/claude/notes/2026-08-18-передача-состояния.md` — «зелёный CI не
означает работающую машину» — здесь применимо буквально.
**2. Обновление дашборда не троттлится (app.js:444448).**
```js
const eventType = (ev.type || '').toLowerCase();
if (eventType.startsWith('link.') || eventType.startsWith('agent.')) {
loadDashboardData(false);
}
```
Каждое событие `link.*` или `agent.*` вызывает `loadDashboardData`, а тот
делает два запроса — `/api/v1/control/agents` и `/api/v1/control/links`
(app.js:230233). Ни debounce, ни throttle в файле нет: `setTimeout`
встречается трижды и ни разу для этой цели.
Согласование политик Links в Control работает непрерывно (`CHANGELOG.md`,
Unreleased, #11). Пачка событий согласования по флоту превращается в
двукратно умноженный поток запросов от каждой открытой вкладки консоли.
На одном узле незаметно, на флоте — усилитель нагрузки на Control.
**3. Мелкое: запасной `generateUuid` на `Math.random()` (app.js:167).**
Для ключа идемпотентности приемлемо — это не секрет. Отмечаю только чтобы
запасная ветка не была скопирована туда, где нужна криптостойкость.
## Что не сделано
- `dotnet build` и `dotnet test` не запускались: на машине нет .NET SDK
(`dotnet: command not found` и в Git Bash, и в PowerShell). Заявленные
в описании PR 158 пройденных тестов и зелёные прогоны CI я принимаю по
ссылкам на прогоны, а не по собственному запуску;
- консоль в браузере не открывалась, поведение вручную не проверялось;
- PR не слит: ветка отстала от `main` (`mergeStateStatus = BEHIND`),
и решение о слиянии за владельцем.
## Решение
Работа соответствует заданию по составу и границам, дефектов безопасности
в ней не найдено. К слиянию пригодна после обновления относительно `main`.
Замечание 1 не блокирует слияние, но должно стать отдельным заданием: без
поведенческих тестов следующая правка `app.js` не будет ничем удержана.
Замечание 2 — отдельным заданием на исправление.
## Вопросы к приёмке
- Обновлять ветку относительно `main` силами `claude` или возвращать автору?
- Выпускать ли задания по замечаниям 1 и 2 сразу, или после слияния #63.

View file

@ -0,0 +1,82 @@
# Поведенческие тесты веб-консоли
- **Кому:** `antigravity`
- **Дата:** 2026-08-19
- **От кого:** главный агент (`claude`), по разбору PR #63
`agents/claude/done/2026-08-18-review-pr63-console-links.md`
- **Ветка:** `antigravity/console-behaviour-tests`
- **Файл отчёта:** `../done/2026-08-19-console-behaviour-tests.md`
## Что нужно сделать
Покрыть тестами поведение веб-консоли, а не наличие разметки.
Сейчас `tests/ServerMonitorManager.Control.Tests/WebConsoleTests.cs` состоит
из проверок вида `Assert.Contains("id=\"create-link-btn\"", html)`. Такие
тесты проходят и при полностью нерабочем `app.js`: они доказывают, что в
`index.html` есть элемент с нужным идентификатором, и ничего больше. При этом
в PR #63 977 строк изменений — почти целиком логика на JavaScript.
Нужны тесты, падающие при поломке поведения. Как минимум:
1. **Создание Link** — форма отправляет `POST /api/v1/control/links` с
`sourceNodeId`, `targetNodeId`, `protocol`, `port`, `ttl`, `reason` и
заголовком идемпотентности; повторная отправка той же формы использует
**тот же** ключ, а новое открытие формы — новый.
2. **Отклонение неверного ввода до отправки** — совпадающие источник и
назначение, пустой `reason`, порт вне 165535: запрос не уходит.
3. **Отключение Link**`POST /api/v1/control/links/{id}/disable`,
и повторное нажатие на уже отключённом Link не отправляет запрос.
4. **Разбор потока событий** — событие, разорванное на границе чанка NDJSON,
собирается и попадает в журнал целиком; строка, не являющаяся JSON,
не роняет поток.
5. **Ограничение журнала** — после 150 событий в списке остаётся 100,
и остаются последние.
6. **Разбор Problem Details** — ответ `400` с `detail` показывается
оператору текстом, а не молча.
Способ проверки выбираешь сам и обосновываешь в отчёте: тесты на JS-движке,
проверка через headless-браузер в CI или вынос логики из `app.js` в
тестируемый модуль. Требование одно: тест должен падать, когда поведение
сломано. Если для этого нужна новая зависимость или шаг в CI — предложи в
отчёте, не добавляй молча.
## Замечено при показе консоли
Мелкий дефект текста, найденный при прогоне консоли на стенде. Исправить
здесь же, отдельного задания он не стоит.
Строка подтверждения направления заканчивается двумя точками:
```
Открытие доступа: с узла «demo-hub» к узлу «demo-home», протокол TCP, порт 443, на 60 мин..
```
Сокращение «мин.» уже содержит точку, и к нему добавляется точка
предложения. Тест из пункта 1 должен проверять эту строку целиком, тогда
дефект и не вернётся.
## Границы
Только тесты и, если это необходимо для тестируемости, реорганизация
`src/ServerMonitorManager.Control/wwwroot/app.js` без изменения поведения.
Не менять эндпоинты Control, не трогать `deploy/**`, релизные workflow,
Desktop и bootstrap. Троттлинг обновления дашборда — отдельное задание
`2026-08-19-console-events-throttle.md`, здесь его не делать.
## Как проверить результат
- каждый новый тест предъявить дважды: зелёным на исправном коде и **красным**
на намеренно сломанном поведении. В отчёт — вывод обоих прогонов. Тест,
который не показан падающим, ничего не доказывает;
- `dotnet test` целиком — зелёный, с выводом;
- прогоны CI на PR — ссылками.
## Контекст и ограничения
Соглашение 6 из `agents/claude/notes/2026-08-18-передача-состояния.md`:
зелёный CI не означает работающую машину. Этим заданием закрывается именно
такой разрыв, поэтому «тесты добавлены и проходят» критерием приёмки не
является — нужен показанный отказ.
Формат отчёта — раздел в описании PR плюс файл в `done/`.

View file

@ -0,0 +1,82 @@
# Троттлинг обновления дашборда по событиям
- **Кому:** `antigravity`
- **Дата:** 2026-08-19
- **От кого:** главный агент (`claude`), по разбору PR #63
`agents/claude/done/2026-08-18-review-pr63-console-links.md`
- **Ветка:** `antigravity/console-events-throttle`
- **Файл отчёта:** `../done/2026-08-19-console-events-throttle.md`
## Что нужно сделать
Убрать усиление нагрузки на Control при пачке событий.
В `src/ServerMonitorManager.Control/wwwroot/app.js` обработчик события
делает так:
```js
const eventType = (ev.type || '').toLowerCase();
if (eventType.startsWith('link.') || eventType.startsWith('agent.')) {
loadDashboardData(false);
}
```
`loadDashboardData` выполняет два запроса — `/api/v1/control/agents` и
`/api/v1/control/links`. Ни debounce, ни throttle в файле нет: `setTimeout`
встречается трижды и ни разу для этой цели. Согласование политик Links в
Control работает непрерывно (`CHANGELOG.md`, Unreleased, #11), поэтому пачка
событий согласования по флоту превращается в удвоенный поток запросов от
**каждой** открытой вкладки консоли.
Нужно свести пачку событий к одному обновлению. Интервал выбираешь сам и
обосновываешь в отчёте; требование — консоль остаётся отзывчивой на
одиночное событие и не отправляет запрос на каждое событие в пачке.
Одновременные обновления не должны накладываться: пока запрос в полёте,
следующий не начинается, но и не теряется.
## Границы
Только `app.js`. Не менять эндпоинты Control, разметку, стили, серверную
часть, `deploy/**` и релизные workflow. Поведенческие тесты консоли —
отдельное задание `2026-08-19-console-behaviour-tests.md`.
## Как проверить результат
- предъявить количество запросов на пачке событий до и после правки:
на N событий подряд должно уйти одно обновление, а не N. В отчёт — способ
измерения и числа;
- показать, что одиночное событие по-прежнему обновляет дашборд;
- `dotnet test` — зелёный, с выводом;
- прогоны CI на PR — ссылками.
«Стало лучше» без чисел результатом проверки не является.
## Измерение, уже проведённое
Дефект найден чтением кода, но затем воспроизведён и измерен на стенде:
настоящие файлы консоли из `wwwroot` под локальным макетом Control API.
Числа получены из журнала сетевых запросов браузера.
- **Установившийся режим.** Одно событие → одно `loadDashboardData` → два
запроса (`/agents` и `/links`). Слияния соседних событий нет ни одного.
- **Пачка при подключении.** Поток отдал 15 накопленных событий разом —
консоль выпустила 22 обновления дашборда, то есть **44 запроса примерно
за 0,1 секунды**. Это одна вкладка и четыре узла.
- **Побочный эффект.** Каждое обновление перестраивает `innerHTML` таблицы
Links целиком. Во время пачки ссылки на элементы строки становятся
недействительными, и таблица перестраивается под курсором оператора.
Воспроизведение: открыть консоль под макетом Control, отдающим backlog
событий при подключении к `/api/v1/control/events`, и посмотреть журнал
сетевых запросов.
Отсюда следует уточнение к проверке: измерять надо именно пачку, а не
установившийся режим — в установившемся режиме при одном событии в 6 секунд
разница незаметна.
## Контекст и ограничения
Измерение сделано на макете API, а не на живом Hub: скорость поступления
событий на настоящем флоте не проверена. Если на живой машине окажется, что
пачек не бывает и усиления нет, — так и напиши в отчёте с числами; это
допустимый результат, а не провал задания.

View file

@ -0,0 +1,110 @@
# Метрики и короткая история узлов в веб-консоли
- **Кому:** `antigravity`
- **Дата:** 2026-08-20
- **От кого:** главный агент (`claude`), по задаче владельца «консоль надо
сделать так же, как и Windows-программа»
- **Ветка:** `antigravity/console-node-metrics`
- **Файл отчёта:** `../done/2026-08-20-console-node-metrics.md`
## Зачем
Windows-клиент на странице «Серверы» показывает по каждому серверу CPU,
память, диск, задержку, состояние здоровья и статус
(`Pages/ServersPage.xaml`: `CpuText`, `MemoryText`, `DiskText`,
`LatencyText`, `HealthText`, `Status`). Веб-консоль не показывает ничего из
этого: в ней есть только имя узла, состояние, версия агента, время
heartbeat и остаток срока сертификата.
Это первая из нескольких задач по сближению консоли с Windows-клиентом.
Здесь закрывается ровно метрики и короткая история — остальное перечислено
ниже и в эту задачу не входит.
## Установленный факт, от которого отталкиваемся
Данные **уже есть на Hub**, показывать нечем.
`ControlStore` содержит таблицу (`ControlStore.cs:81`):
```sql
CREATE TABLE IF NOT EXISTS metric_samples (
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
node_id TEXT NOT NULL REFERENCES agents(node_id) ON DELETE CASCADE,
recorded_at TEXT NOT NULL,
payload_json TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS ix_metric_samples_node_time
ON metric_samples(node_id, recorded_at DESC);
```
Наполняется она из heartbeat агента, чистится по `MetricRetentionHours`
(по умолчанию 168 часов). При этом в группе `/api/v1/control` **нет ни
одного маршрута для метрик** — я перечитал все `control.Map*` в
`Program.cs`. То есть оператор физически не может их получить.
## Что нужно сделать
1. **Добавить в Control чтение метрик под ролью Operator.** Как минимум
последний срез по каждому узлу и история по одному узлу за period.
Форму запроса, имена полей и ограничение на объём выбираешь сам и
обосновываешь в отчёте. Обязательные требования: маршрут в группе
`control` (то есть `RequireAuthorization("Operator")`), ограничение
количества возвращаемых точек, и никакого доступа к чужим данным по
роли Agent или Automation.
2. **Показать это в консоли** в разделе узлов: текущие CPU, память, диск и
задержка рядом с узлом, плюс короткая история — так же, как её
показывает Windows-клиент.
3. **`payload_json` — данные из внешнего источника.** Он приходит от
агента. Разбирать его в консоли только через уже имеющийся
`escapeHtml`, как это сделано с payload событий; ни одной вставки в
`innerHTML` без экранирования. Отсутствующие или битые поля не должны
ронять таблицу узлов.
## Границы — что переносить нельзя
**SSH-терминал и приватные ключи в веб-консоль не переносятся.** Это не
вопрос трудоёмкости, а заявленное свойство продукта: README обещает
«opens direct SSH terminals without sending a private terminal key to the
Hub», а ключ мониторинга и сертификат оператора защищены DPAPI и лежат
только на Windows-машине. Страница Sessions остаётся у Windows-клиента.
Также не входит в эту задачу и делается отдельно: страница Settings,
группы и теги серверов, alert rules и журнал уведомлений (последние два
пункта в `docs/roadmap.md` числятся невыполненными и для Windows-клиента).
Не трогать: `deploy/**`, релизные workflow, Desktop, схему существующих
таблиц (добавление индексов допустимо и обосновывается).
## Как проверить результат
- новый маршрут закрыт ролью: запрос без токена → 401, под ролью Agent и
Automation → 403, под Operator → 200. Три теста, вывод в отчёт;
- ограничение объёма истории проверено тестом: запрос заведомо большего
количества точек возвращает не больше предела;
- битый или неполный `payload_json` не роняет ни маршрут, ни отрисовку —
тест на каждую сторону;
- `dotnet test` целиком зелёный, с выводом;
- прогоны CI на PR — ссылками.
## Связь с другими заданиями
Эта задача пересекается с двумя уже выданными и делать её нужно **после**
них, иначе правки лягут друг на друга в одном файле:
- `2026-08-19-console-behaviour-tests.md` — поведенческие тесты консоли.
Новая функциональность обязана прийти с такими тестами сразу, а не
проверками строк в HTML;
- `2026-08-19-console-events-throttle.md` — троттлинг обновления дашборда.
Метрики увеличат объём каждого обновления, поэтому без троттлинга
усиление нагрузки станет заметнее.
Если считаешь, что порядок должен быть другим, — скажи в отчёте и обоснуй,
это решение приму.
## Контекст и ограничения
Проверено чтением кода и запуском Control локально на пустой базе: узлов
ноль, метрик ноль. Как выглядят настоящие `payload_json` от живого агента,
я не видел — на живом Hub не проверял. Прежде чем фиксировать контракт
ответа, посмотри фактическую форму payload в коде агента, а не в моём
описании.

View file

@ -0,0 +1,101 @@
# Согласование Links дёргает привилегированный процесс каждые 30 секунд
- **Кому:** `antigravity`
- **Дата:** 2026-08-20
- **От кого:** главный агент (`claude`)
- **Ветка:** `antigravity/reconciliation-poll-interval`
- **Файл отчёта:** `../done/2026-08-20-reconciliation-poll-interval.md`
## Что происходит
`LinkReconciliationBackgroundService` тикает с периодом
`Math.Min(LinkReconciliationSeconds, 30)` (строка 117), то есть **не реже
раза в 30 секунд при любой настройке**. На каждом тике первым делом
безусловно вызывается `applier.GetReconciliationRequestAsync(...)`
(строка 30) — а это запуск внешнего процесса
`/usr/bin/sudo` + `ochenstarik-smm-policy-apply`
(`LinkPolicyApplier.RunAsync`, строка 143).
Отсюда два следствия.
**1. Настройка не ограничивает привилегированные вызовы.**
`LinkReconciliationSeconds` валидируется в диапазоне 30…3600, и оператор,
поставивший 3600, вправе ожидать примерно одну активность в час. Фактически
он получает около **120 запусков sudo в час** — интервал управляет только
полным проходом `ReconcileAllAsync`, но не инспекцией маркера.
**2. Backoff не подавляет эти вызовы.** Проверка `_backoffUntil`
(строки 3538) стоит **после** обращения к маркеру. Когда firewall
недоступен и служба ушла в backoff, она всё равно продолжает запускать
sudo каждые 30 секунд — ровно в той ситуации, когда предполагалось
притормозить.
Каждая неудачная инспекция пишет предупреждение с полным стеком.
## Измерение
Control запущен локально с `--Control:LinkReconciliationSeconds=3600`.
Журнал приложений Windows, провайдер `.NET Runtime`, категория
`ServerMonitorManager.Control.LinkReconciliationBackgroundService`:
```
17:29:16
17:29:46
17:30:16
17:30:46
17:31:16
```
Ровно каждые 30 секунд при заданных 3600. Записи идут непрерывно, службы
в этот момент нечем было заниматься: узлов ноль, связей ноль.
## Что нужно сделать
Привести поведение в соответствие с настройкой, **не потеряв быструю
реакцию на маркер** — она и есть смысл 30-секундного опроса, её ломать
нельзя.
Как минимум:
1. Не обращаться к маркеру, пока действует `_backoffUntil`. Сейчас backoff
не защищает ни от чего.
2. Решить и обосновать в отчёте, каким должен быть контракт
`LinkReconciliationSeconds`. Либо это интервал полного прохода, и тогда
частота опроса маркера должна настраиваться отдельным параметром с
собственной валидацией и описанием. Либо это верхняя граница любой
активности, и тогда опрос обязан её соблюдать.
3. Сделать так, чтобы повторяющаяся недоступность helper'а не писала
полный стек на каждом тике.
Выбор между вариантами пункта 2 — за тобой, но он должен быть **выбором с
обоснованием**, а не молчаливым изменением поведения.
## Границы
`src/ServerMonitorManager.Control/LinkReconciliationBackgroundService.cs`,
при необходимости `ControlOptions` и валидация в `Program.cs`, плюс тесты
`tests/ServerMonitorManager.Control.Tests/LinkReconciliationTests.cs`.
Не трогать `LinkService.ReconcileAllAsync`, helper в `deploy/**`, консоль
и Desktop.
## Как проверить результат
- `dotnet test tests/ServerMonitorManager.Control.Tests/...` — все 158
существующих тестов зелёные, с выводом. Маркерные тесты трогать только
если меняется контракт, и тогда объяснить каждое изменение;
- новый тест на то, что во время backoff обращения к маркеру **не
происходит**: подменить `ILinkPolicyApplier` и сосчитать вызовы;
- новый тест на соблюдение настроенного интервала по выбранному контракту;
- предъявить число вызовов applier за фиксированное время до и после
правки — как в измерении выше, числами.
## Контекст и ограничения
Дефект найден на живом прогоне Control на Windows, где helper'а нет вовсе,
поэтому каждая инспекция падает. На настоящем Hub helper есть и вызовы
успешны — значит там это не ошибки в журнале, а тихая нагрузка: запуск
sudo и внешнего процесса дважды в минуту круглосуточно. Оценить, насколько
это существенно на настоящем Hub, я не могу: живого Hub под рукой нет.
Если при проверке окажется, что нагрузка пренебрежима и контракт менять не
нужно, — так и напиши с числами, это допустимый результат. Пункт 1 про
backoff при этом всё равно остаётся к исправлению.

View file

@ -0,0 +1,151 @@
# Отчёт: обновление зависимостей GitHub Actions и NuGet
- **Задание:** `../inbox/2026-08-18-deps-update.md`
- **Агент:** `claude`
- **Дата:** 2026-08-18
- **Ветка / коммиты:** `claude/deps-update`. Правок workflow эта ветка не
несёт — см. «Как доставлено».
## Что сделано
Разобрано отставание зависимостей и проверены три открытых PR Dependabot по
GitHub Actions. Проверка была содержательной, а не сводилась к «CI зелёный».
- `actions/download-artifact` v4.1.8 → v8.0.1 (PR #46). Затрагивает одну
точку — `linux-release.yml:256`; это была последняя старая ссылка в дереве,
`linux-platform-matrix.yml` уже использовал v8.0.1 (SHA `3e5f45b2…`).
Обновление приводит репозиторий к одной версии.
- `docker/setup-qemu-action` → v4.2.0 (PR #44). Одна точка,
`linux-control-agent.yml:83`; прежняя ссылка была закреплена по SHA
`c7c53464…` с комментарием `# v3` без минорной версии.
- `sigstore/cosign-installer` v3.5.0 → v4.1.2 (PR #45). **Не принят**,
причина в разделе «Вопросы к приёмке».
Все три правки я предварительно применял локально и построчно сверял с
ветками Dependabot — совпадение полное. Затем правки из ветки сняты, чтобы
не применить одно и то же дважды: доставка идёт слиянием PR Dependabot.
NuGet не обновлялся — см. «Что не сделано».
## Как доставлено
Решением владельца от 2026-08-18: сливаются PR #44 и #46, правки из этой
ветки сняты. В ветке остаются только задания, отчёты и записка передачи
состояния. PR #45 остаётся открытым.
## Как проверено
Сверка применённых правок с ветками Dependabot (до снятия правок):
```
$ git diff -- .github/ | grep -E '^[+-][^+-]' | sort > mine.lines
$ for b in dependabot/github_actions/actions/download-artifact-8.0.1 \
dependabot/github_actions/sigstore/cosign-installer-4.1.2 \
dependabot/github_actions/docker/setup-qemu-action-4.2.0; do
git diff origin/main...origin/$b -- .github/
done | grep -E '^[+-][^+-]' | sort > pr.lines
$ diff pr.lines mine.lines
СОВПАДАЕТ построчно
```
Старых версий после применения в дереве не оставалось:
```
$ grep -rn "download-artifact@fa0a91b8\|cosign-installer@v3\|setup-qemu-action@c7c53464" .github/workflows/
старых версий не осталось
```
YAML всех шести workflow разбирается:
```
$ for f in .github/workflows/*.yml; do python -c "import sys,yaml;yaml.safe_load(open(sys.argv[1],encoding='utf-8'));print('OK',sys.argv[1])" "$f"; done
OK .github/workflows/linux-control-agent.yml
OK .github/workflows/linux-platform-matrix.yml
OK .github/workflows/linux-release.yml
OK .github/workflows/release-verification.yml
OK .github/workflows/windows-build.yml
OK .github/workflows/windows-release.yml
```
Текущее состояние ветки — правок workflow нет:
```
$ git diff 23eb3f5 --stat -- .github/
(пусто)
```
## Что не сделано
**NuGet не обновлён — нет .NET SDK на машине.**
```
$ dotnet --version
bash: dotnet: command not found
PS> Get-Command dotnet
dotnet not found on PATH
```
В `Directory.Build.props` включён `RestorePackagesWithLockFile=true`, в CI
сборка идёт с `-p:RestoreLockedMode=true`, и в дереве семь
`packages.lock.json`. Поднять версию в `Directory.Packages.props` без
пересборки lock-файлов — гарантированно уронить CI. Без SDK пересобрать их
нечем, поэтому файлы не тронуты.
Отставание по данным `api.nuget.org` на 2026-08-18 (запрошены версии,
`dotnet list package --outdated` не запускался):
| Пакет | В репозитории | Актуальная |
|-------|---------------|------------|
| Microsoft.AspNetCore.Authentication.Certificate | 10.0.10 | 10.0.11 |
| Microsoft.AspNetCore.Mvc.Testing | 10.0.10 | 10.0.11 |
| Microsoft.Data.Sqlite | 10.0.10 | 10.0.11 |
| Microsoft.Extensions.Configuration.* (4 пакета) | 10.0.10 | 10.0.11 |
| System.Security.Cryptography.ProtectedData | 10.0.0 | 10.0.11 |
| Microsoft.NET.ILLink.Tasks (`Directory.Build.props`) | 10.0.10 | 10.0.11 |
| Microsoft.Windows.SDK.BuildTools | 10.0.28000.2270 | 10.0.28000.2526 |
| Microsoft.Windows.SDK.BuildTools.WinApp | 0.4.0 | 0.6.0 |
| Microsoft.WindowsAppSDK | 2.2.0 | 2.4.0 |
| Microsoft.NET.Test.Sdk | 18.8.1 | 18.9.0 |
| xunit.v3 | 3.2.2 | 4.0.0 (мажорная) |
| xunit.runner.visualstudio | 3.1.5 | 4.0.0 (мажорная) |
| SQLitePCLRaw.bundle_e_sqlite3 | 3.0.5 | 3.0.5 — актуален |
Открытых PR Dependabot по NuGet нет, хотя в `.github/dependabot.yml`
экосистема `nuget` настроена с недельным интервалом. Причина не определена.
## Замечено рядом
Вне границ задачи; предлагаю отдельными заданиями.
1. **`sigstore/cosign-installer` закреплён тегом, а не SHA.** Все остальные
действия в репозитории закреплены по коммиту с комментарием версии;
cosign-installer — единственное исключение, и это единственное действие,
от которого зависит подпись релиза. Тег подвижен.
2. **`Microsoft.Windows.SDK.BuildTools.WinApp` 0.4.0** — версия ниже 1.0,
мажорных гарантий совместимости нет; переход на 0.6.0 делать отдельно.
3. **xunit 3 → 4 — мажорный переход** для двух пакетов, отдельной задачей
с прогоном тестов.
## Вопросы к приёмке
**PR #45 (`cosign-installer` 3.5.0 → 4.1.2) не принят к слиянию.**
Зелёные проверки на #45 и #46 про релиз ничего не доказывают:
`linux-release.yml` запускается только по `push` тега `v*` и по
`workflow_dispatch` (строки 312), на pull request он не выполняется вовсе.
Мажорная смена installer меняет версию cosign у producer, а
`deploy/ochenstarik-server-monitor-manager.sh:34` фиксирует у consumer
`COSIGN_VERSION="v3.1.3"` с проверкой SHA-256. Какую версию cosign ставит
installer 4.1.2 по умолчанию — не проверял, это не определено.
`docs/release-policy.md` показывает четыре поломки контракта
producer/consumer по cosign: alpha.12, alpha.13, alpha.17, alpha.18.
Предлагаю до слияния #45 прогнать `Release pipeline` через
`workflow_dispatch` на репетиционном теге — с оговоркой из
`agents/claude/notes/2026-08-18-передача-состояния.md`: репетиция идёт по
`refs/heads/main` и пропускает шаги под условием `refs/tags/*`, так был
сожжён alpha.19. Значит проверять нужно ещё и то, что нужные шаги вообще
исполнялись.
Второй вопрос: обновлять ли NuGet отдельным заданием на машине с .NET SDK.

View file

@ -0,0 +1,104 @@
# Отчёт: синхронизация git-состояния репозитория
- **Задание:** `../inbox/2026-08-18-git-sync.md`
- **Агент:** `claude`
- **Дата:** 2026-08-18
- **Ветка / коммиты:** `claude/deps-update`, ответвлена от `23eb3f5`
(`chore/agents-workspace`). Изменений кода в рамках этой задачи нет.
## Что сделано
Выполнена синхронизация с `origin` и снято фактическое состояние. Файлы
репозитория этой задачей не менялись — сводить оказалось нечего.
Установлено:
- `git fetch --all --prune` подтянул одну новую удалённую ветку
`origin/antigravity/alpha12-ci-fix`; удалённых на сервере веток,
подлежащих очистке локально, не было;
- локальная `main` и `origin/main` — один и тот же коммит `f8e751c`;
- `chore/agents-workspace` опережает `origin/main` на 2 коммита и не отстаёт
ни на один: `git merge-base origin/main HEAD` = `f8e751c` = `origin/main`.
Вливать `main` в ветку не требуется;
- незапушенных локальных коммитов и stash нет.
Состояние открытых PR на 2026-08-18:
| PR | Ветка | Слияние | Проверки |
|----|-------|---------|----------|
| #64 | `chore/agents-workspace` | `CLEAN` | 4/4 успешно |
| #63 | `antigravity/console-links` | `BEHIND` — отстала от `main` | 12/12 успешно |
| #46 | `dependabot/.../download-artifact-8.0.1` | `CLEAN` | 4/4 успешно |
| #45 | `dependabot/.../cosign-installer-4.1.2` | `CLEAN` | 4/4 успешно |
| #44 | `dependabot/.../setup-qemu-action-4.2.0` | `CLEAN` | 4/4 успешно |
Удалённые ветки, полностью влитые в `origin/main` и пригодные к удалению:
`antigravity/desktop-update-proof`, `antigravity/node-enrollment-endpoint`,
`antigravity/password-login`, `antigravity/web-console`,
`codex/cosign-provisioning`, `codex/release-alpha16-cosign-verification`,
`codex/release-alpha17-machine-output`,
`codex/release-alpha18-cosign-negative-test`, `codex/release-alpha20`,
`codex/uninstall-mode` — 10 веток.
Не влита и при этом не имеет открытого PR: `antigravity/alpha12-ci-fix`.
Её назначение не определено.
## Как проверено
```
$ git fetch --all --prune
From https://github.com/ochenstarik-ui/server-monitor-manager
* [new branch] antigravity/alpha12-ci-fix -> origin/antigravity/alpha12-ci-fix
$ git rev-parse main origin/main
f8e751c4bc827b18c6834063cd2742e832d798d9
f8e751c4bc827b18c6834063cd2742e832d798d9
$ git merge-base origin/main HEAD
f8e751c4bc827b18c6834063cd2742e832d798d9
$ git log --oneline HEAD..origin/main
(пусто)
$ git log --oneline origin/main..HEAD
23eb3f5 chore(agents): разбор рабочих папок с диска на 2026-08-18
26c60a3 chore(agents): единая структура заданий и приёмки
$ git stash list
(пусто)
$ git log --branches --not --remotes --oneline
(пусто)
```
## Что не сделано
- PR не слиты и ветки не запушены: по `AGENTS.md` это делает главный агент,
и владелец отдельного подтверждения не давал;
- 10 влитых удалённых веток не удалены — удаление на `origin` необратимо
и требует подтверждения владельца;
- PR #63 не переведён в актуальное состояние относительно `main`: это ветка
агента `antigravity`, чужую работу не трогаю.
## Замечено рядом
Вне границ задачи; предлагаю отдельными заданиями.
1. **CHANGELOG отстал на 14 релизов.** `CHANGELOG.md` заканчивается на
`v0.1.0-alpha.6` (2026-07-31), теги идут до `v0.1.0-alpha.20`
(2026-08-18). Секция `Unreleased` ссылается на давно слитые #10#12.
2. **README называет неверную текущую версию.** `README.md:101`
«`v0.1.0-alpha.14` is an early testing release».
3. **`docs/release-policy.md` доведён до alpha.19**, запись про alpha.20
отсутствует, хотя документ ведёт построчную историю каждой версии.
4. **`docs/roadmap.md` не отражает слитые в `main` подсистемы**: web console,
password login для консоли, uninstall mode, unified installer.
5. **Ветка `antigravity/alpha12-ci-fix`** висит без PR — либо оформить PR,
либо удалить.
## Вопросы к приёмке
- Удалять ли 10 влитых удалённых веток и кем — этим же заданием или
отдельным?
- Нужно ли обновлять PR #63 относительно `main` силами `claude`, или это
возвращается автору ветки?

View file

@ -0,0 +1,87 @@
# Отчёт: документация приведена в соответствие с историей релизов
- **Задание:** `../inbox/2026-08-20-docs-sync.md`
- **Агент:** `claude`
- **Дата:** 2026-08-20
- **Ветка:** `claude/deps-update`
## Что сделано
**`CHANGELOG.md`.** Дописаны 14 отсутствовавших версий, alpha.7 … alpha.20.
Записи выведены из тегов и коммитов между ними, а не из памяти. Прежняя
секция `Unreleased` со ссылками на #10#12 оказалась содержимым alpha.7 —
перенесена туда. Новый `Unreleased` описывает слитое после alpha.20: #63,
#64, #44, #46. Ссылки сравнения версий перестроены для всех 20 тегов.
Сожжённые номера помечены прямо в заголовке — `burned, not published`с
указанием, чем упал пайплайн и под какой версией вышло их содержимое:
- alpha.10 и alpha.11 — пайплайн упал до публикации;
- alpha.19 — `DEFAULT_RELEASE_TAG` в упакованном установщике не совпал
с тегом; содержимое вышло под alpha.20.
Для опубликованных, но дефектных версий записи содержат сам дефект:
alpha.8 (осиротевшая подпись без manifest v2), alpha.12 и alpha.13
(подпись без сертификата Fulcio), alpha.14 (проверяемая, но не
устанавливаемая на чистый хост), alpha.15 … alpha.17 (на чём остановилась
проверка). Иначе CHANGELOG подсказывал бы ставить версию, которая не
ставится.
**`docs/release-policy.md`.** Добавлена запись про alpha.20 — что он
исправляет, что переносит из сожжённого alpha.19.
**`docs/roadmap.md`.** Веб-консоли в плане не было вовсе, поэтому это не
переключение галочек, а дописывание фактически поставленного. В этап 2:
консоль оператора, отдача ассетов из встроенных ресурсов, вход по паролю
для тестовых стендов. В этап 3: управление Links из консоли, аварийное
отключение, раздельные желаемое/фактическое состояния, журнал событий.
В этап 7: режим полного удаления сервера, пошаговый объединённый установщик.
## Как проверено
Все 20 версий на месте:
```
$ grep -c "^## \[v0.1.0-alpha" CHANGELOG.md
20
```
Даты записей сверены с датами тегов — расхождений нет:
```
$ for n in $(seq 1 20); do t="v0.1.0-alpha.$n"; ... done
v0.1.0-alpha.1 2026-07-16 2026-07-16 совпадает
...
v0.1.0-alpha.20 2026-08-18 2026-08-18 совпадает
```
Двадцать строк, все «совпадает».
## Что не сделано
**README и одиннадцать переводов не тронуты намеренно.** `README.md:101`
по-прежнему называет `v0.1.0-alpha.14`. `docs/release-policy.md` отдаёт
строку статуса релиза в корневом README и в переводах в единоличное
владение владельцу релиза и прямо требует запрашивать изменение, а не
править самому. Запрошено заданием
`agents/codex/inbox/2026-08-20-readme-release-status.md`.
Порядок регламента нарушен: задание в `inbox/` записано после выполнения
работы, а не до. Указываю прямо, чтобы это не выглядело соблюдённым.
## Замечено рядом
1. **`README.md:101` и переводы** — предмет выданного задания Codex.
2. **В roadmap этап 6 содержит пункт «синхронизировать все переводы README
с текущим release status»** — он остаётся невыполненным и закрывается
тем же заданием.
3. **Содержание записей CHANGELOG выведено из заголовков коммитов и
merge-коммитов.** Там, где заголовок был неинформативен, запись вышла
короче фактического объёма работы. Это не выдумка, но и не полный
пересказ: восстановление постфактум точнее заголовков быть не может.
## Вопросы к приёмке
Записи alpha.10, alpha.11 и alpha.19 оставлены в CHANGELOG как сожжённые.
Альтернатива — не упоминать их вовсе. Считаю упоминание правильным:
читатель, увидев разрыв в нумерации, иначе решит, что запись потеряна.

View file

@ -0,0 +1,45 @@
# Обновление зависимостей: GitHub Actions и NuGet
- **Кому:** `claude`
- **Дата:** 2026-08-18
- **От кого:** владелец проекта (задание получено в чате: «актуализируй проект»,
уточнено выбором «Обновить зависимости»)
- **Ветка:** `claude/deps-update`
- **Файл отчёта:** `../done/2026-08-18-deps-update.md`
## Что нужно сделать
Закрыть отставание зависимостей:
1. GitHub Actions — три открытых PR от Dependabot (#44 `docker/setup-qemu-action`
3.7.0 → 4.2.0, #45 `sigstore/cosign-installer` 3.5.0 → 4.1.2,
#46 `actions/download-artifact` 4.1.8 → 8.0.1).
2. NuGet — версии в `Directory.Packages.props` и `Directory.Build.props`
против актуальных на nuget.org.
## Границы
Не мержить PR Dependabot и не пушить ветку без подтверждения владельца.
Не менять способ закрепления действий (tag → SHA) — это отдельная задача,
даже если несоответствие очевидно. Не править код проекта, документацию
и CHANGELOG.
## Как проверить результат
- Обновлённые SHA в `.github/workflows/` совпадают с SHA из соответствующих
веток Dependabot: `git diff origin/main...origin/dependabot/...`;
- `grep -rn "uses:" .github/workflows/` — в дереве не осталось старых версий;
- для NuGet: `dotnet list package --outdated` и восстановление
`packages.lock.json` (`RestorePackagesWithLockFile=true`,
в CI `-p:RestoreLockedMode=true`).
## Контекст и ограничения
Релизная подпись зависит от cosign: `deploy/ochenstarik-server-monitor-manager.sh`
закрепляет потребительский `COSIGN_VERSION="v3.1.3"` с проверкой SHA-256,
а CI ставит cosign через `sigstore/cosign-installer` без закрепления версии
cosign. `docs/release-policy.md` (alpha.17, alpha.18) описывает, что контракт
producer/consumer по cosign уже ломался. Смена версии installer — решение
владельца, не самостоятельное.
Наличие .NET SDK на рабочей машине на момент постановки задачи не определено.

View file

@ -0,0 +1,34 @@
# Синхронизация git-состояния репозитория
- **Кому:** `claude`
- **Дата:** 2026-08-18
- **От кого:** владелец проекта (задание получено в чате: «актуализируй проект»,
уточнено выбором «Синхронизировать git»)
- **Ветка:** `claude/deps-update`
- **Файл отчёта:** `../done/2026-08-18-git-sync.md`
## Что нужно сделать
Установить фактическое расхождение локального репозитория и `origin`, свести
локальные ветки со свежим `main` и предъявить состояние открытых PR и
удалённых веток: что влито, что отстало, что можно удалять.
## Границы
Не пушить, не мержить PR, не удалять удалённые ветки без отдельного
подтверждения владельца. Не трогать ветки других агентов
(`antigravity/*`, `codex/*`, `hermes/*`). Не править документацию —
рассинхрон CHANGELOG/README/roadmap с фактическим состоянием релизов
вынесен из этой задачи и идёт в отчёт списком.
## Как проверить результат
- `git fetch --all --prune` — выполнен;
- `git rev-parse main origin/main` — совпадают;
- `git log --oneline HEAD..origin/main` — пусто (нечего вливать);
- `git branch -r --merged origin/main` — список влитых веток приведён в отчёте.
## Контекст и ограничения
`AGENTS.md`: в `main` пушит и принимает работу только главный агент.
Ветка ведётся с префиксом `claude/` согласно таблице в `agents/README.md`.

View file

@ -0,0 +1,41 @@
# Привести документацию в соответствие с фактическим состоянием релизов
- **Кому:** `claude`
- **Дата:** 2026-08-20
- **От кого:** владелец проекта (в чате: «делай что можно. и продолжай»)
- **Ветка:** `claude/deps-update`
- **Файл отчёта:** `../done/2026-08-20-docs-sync.md`
## Что нужно сделать
`CHANGELOG.md` заканчивался на `v0.1.0-alpha.6` от 2026-07-31, тогда как
теги идут до `v0.1.0-alpha.20`. Секция `Unreleased` ссылалась на давно
слитые #10#12. `docs/release-policy.md` вёл построчную историю версий, но
обрывался на alpha.19. `docs/roadmap.md` не отражал подсистемы, слитые в
`main`.
Восстановить историю по фактам репозитория — тегам и коммитам между ними,
а не по памяти:
- CHANGELOG: записи для alpha.7 … alpha.20, сожжённые номера помечены как
сожжённые, ссылки сравнения версий выправлены;
- release-policy: запись про alpha.20;
- roadmap: отметить фактически поставленное.
## Границы
Не менять код, workflow и версии сборки. **Не трогать строку статуса релиза
в README и переводах**: по `docs/release-policy.md` эти пути принадлежат
владельцу релиза, изменение запрашивается заданием, а не правится напрямую.
## Как проверить результат
- в CHANGELOG присутствуют все 20 версий: `grep -c "^## \[v0.1.0-alpha"`;
- даты записей совпадают с датами тегов: `git log -1 --format=%ad --date=short <тег>`;
- содержание записей выводится из `git log` между соседними тегами.
## Контекст и ограничения
Сожжённые номера `alpha.10`, `alpha.11`, `alpha.19` не переиспользуются:
теги существуют, релизы не опубликованы. Запись о них в CHANGELOG обязана
показывать это прямо, иначе читатель решит, что релиз был.

View file

@ -0,0 +1,114 @@
# Передача состояния: как продолжить работу в новом чате
Этот файл заменяет переписку. Он написан для того, чтобы новый чат начинался
с одного сообщения и не требовал пересказа истории.
## Первое сообщение в новом чате
> Репозиторий `C:\Users\Ochenstarik\Agent_projects\server-monitor-manager`,
> GitHub `ochenstarik-ui/server-monitor-manager`.
> Прочитай `agents/README.md` и `agents/claude/notes/2026-08-18-передача-состояния.md`,
> сверь состояние с GitHub и продолжай как главный агент: проверяешь работу
> Codex и Antigravity, пишешь им задания, мержишь и выпускаешь релизы.
Больше ничего пересказывать не нужно: доска, задания, отчёты и архив лежат
в репозитории.
## Где что лежит
| Что | Где |
|---|---|
| Правила работы агентов | `agents/README.md` |
| Задания агенту | `agents/<agent>/inbox/` |
| Отчёты агента | `agents/<agent>/done/` |
| Принятые пары «задание — отчёт» | `agents/_accepted/ГГГГ-ММ-ДД-имя/` |
| Доска состояния (прежний формат) | `agents/_salvage-2026-08-18/smm-deliverables/README.md` |
| Архив закрытых заданий | `agents/_salvage-2026-08-18/smm-deliverables/Старые задачи/` |
| Инструкция владельцу по серверам | `agents/_salvage-2026-08-18/smm-deliverables/smm-cleanup-and-install-2026-08-15.md` |
Доску стоит перенести из `_salvage` в постоянное место — она живая, а не архив.
## Состояние на 2026-08-18
- `main``f8e751c`, слит PR #62 `codex/release-alpha20`;
- последний релиз — `v0.1.0-alpha.20`;
- открыты: PR #63 `antigravity/console-links` (управление Links в консоли),
PR #64 `chore/agents-workspace` (эта самая схема папок), плюс dependabot;
- ветка `claude/deps-update` содержит незакоммиченные правки двух workflow
и два новых задания в `agents/claude/inbox/`.
### Сожжённые номера версий
`alpha.10`, `alpha.11`, `alpha.19` — теги созданы, `Release pipeline` упал,
релизы не опубликованы, номера не переиспользуются.
`alpha.19` сожжён при мне и по процедурной дыре, а не по вине исполнителя:
шаг `Package bootstrap` сверяет `DEFAULT_RELEASE_TAG` в `deploy/smm-setup.sh`
с выпускаемым тегом, но весь блок закрыт условием `refs/tags/*`, поэтому
репетиция через `workflow_dispatch` идёт по `refs/heads/main` и эту сверку
пропускает. Репетиция была зелёной, тег упал.
**Вывод, который дороже самого случая:** репетиция, не покрывающая шаги,
исполняемые только на теге, не является репетицией. Проверять это до
следующего выпуска.
## Рабочие соглашения
Выведены из происшествий, а не из общих соображений.
1. **Задание содержит только предстоящую работу.** Файлы, начинавшиеся
с описания уже сделанного, исполнитель принимал за отчёт о выполнении:
Codex ответил «задание уже выполнено, PR #58» вместо работы над
невыполненной частью. Сделанное живёт на доске, а не в задании.
2. **Отчёт — раздел в описании PR.** Отдельный файл с отчётом результатом
не является. Antigravity однажды сдал только такой файл, не создав ни ветки,
ни PR.
3. **Проверять по репозиторию, а не по отчёту.** Смотреть диф от базы слияния,
а не от `main`: отставшая ветка в обычном дифе выглядит так, будто удаляет
чужую работу.
4. **Критерий приёмки должен быть достижим.** «Проверка запустилась
автоматически» была недостижима: событие от `GITHUB_TOKEN` не запускает
другие workflow. Это дефект задания, а не исполнителя.
5. **Границы областей прописывать явно.** Codex — `deploy/**`, релизный
пайплайн, проверка релиза. Antigravity — `src/**`, консоль, тесты Control
и Desktop. Пересечение областей ломает параллельную работу.
6. **Зелёный CI не означает работающую машину.** Так найдены: отсутствующий
cosign, права на каталог mesh, отдача ассетов из ресурсов сборки. Каждый раз
тест подставлял временный путь и проходил, а на сервере отказывало.
7. **Команды владельцу помечать, где выполнять**: 🖥 на Windows, 🐧 на сервере.
Путаница машин стоила нескольких заходов.
8. **Долгие загрузки — с индикатором.** Молчащий `curl -fsSL` на 24 МБ дважды
принят за зависание и прерван.
## Живая установка
Проверено на настоящих машинах, а не в CI.
- **Hub** `178.212.13.102` (`host1885995-3.hostland.pro`), Ubuntu 24.04 x86_64,
поставлен из `v0.1.0-alpha.14`;
- отпечаток CA:
`EB:05:E5:16:42:EE:97:28:C2:E9:EA:6F:3E:48:3C:0C:1C:1E:02:E0:ED:9B:AF:7B:BF:24:2C:19:D4:4B:11:C4`;
- публичный ключ WireGuard Hub: `AjN8XwPB11bRQa0fO5ljPlAWc0FxU+fxbeUT8AZczHk=`;
- **Node `ai-agent`**`vm1957808.vds.chsl.one`, адрес `10.77.0.2`, mesh собран,
ping 7.6 мс. На этой машине работает 3x-ui и он не пострадал;
- свободных серверов больше нет. `tests/acceptance/three-server-mesh.sh` требует
Hub и три узла: источник и две цели, потому что смысл проверки в том, что
к одной цели доступ по Link открыт, а ко второй закрыт. На двух машинах
это не проверить;
- WSL под узлы не годится: все дистрибутивы WSL2 делят один сетевой стек,
два `smm0` с разными адресами не поднять. Проверено экспериментом.
**Физическая приёмка — единственный пункт Горизонта 0, не закрываемый кодом.**
## Что важно знать про продукт
- подпись релиза проверяется настоящим материалом с обеих сторон: установкой
на чистый runner (Linux) и `UpdateService` (Windows), автоматически на каждом
релизе;
- проверка подписи требует исходящего доступа к службам sigstore; в закрытом
контуре установка зависнет;
- таблица nftables `inet ochenstarik_smm` цепляется только к хуку `forward`
и только к трафику `smm0 → smm0`. Хука `input` в ней нет, политика `accept`.
Поэтому установка на сервер с 3x-ui безопасна — проверено на живой машине;
- опубликованный тег неизменяем, ассеты не заменяются: исправление выпускается
следующим номером.

View file

@ -0,0 +1,85 @@
# Доказать или отклонить обновление cosign-installer до 4.1.2
- **Кому:** `codex`
- **Дата:** 2026-08-19
- **От кого:** главный агент (`claude`)
- **Ветка:** `codex/cosign-installer-rehearsal`
- **Файл отчёта:** `../done/2026-08-19-cosign-installer-rehearsal.md`
## Что нужно сделать
Открыт PR #45: `sigstore/cosign-installer` 3.5.0 → 4.1.2 в
`.github/workflows/linux-release.yml`, два вхождения (строки 26 и 253).
Слиянию он не подлежит, пока не предъявлено доказательство, что подпись
релиза после смены версии остаётся проверяемой. Нужно это доказательство —
либо обоснованный отказ от обновления.
Ответить требуется на три вопроса, каждый — фактом, а не рассуждением:
1. **Какую версию cosign ставит `cosign-installer@v4.1.2` по умолчанию.**
В workflow версия cosign не закреплена (`cosign-release` не задан), так
что она определяется installer'ом. Сейчас это не определено.
2. **Совместим ли формат подписи, которую произведёт этот cosign, с
потребителями — обоими.** Потребителей два, и они на разных версиях
cosign:
- **Linux.** `deploy/ochenstarik-server-monitor-manager.sh:34` закрепляет
`COSIGN_VERSION="v3.1.3"` с проверкой SHA-256 и проверяет подпись через
`cosign verify-blob` с отдельным сертификатом Fulcio.
- **Windows.** `src/ServerMonitorManager.Desktop/UpdateService.cs:79`
закрепляет `CosignVersion = "v2.4.0"` с закреплённым SHA-256
(`ProcessSignatureVerifier`), скачивает `cosign-windows-amd64.exe` и
выполняет тот же `verify-blob --certificate … --certificate-identity-regexp
…@refs/tags/v.*`.
То есть подпись одного релиза проверяется cosign v3.1.3 на Linux и cosign
v2.4.0 на Windows. Смена версии у producer должна оставить проверяемым
**оба** пути, а не только тот, который гоняет Release Verification.
Разъезд producer и consumer по cosign — ровно то, на чём проект уже
потерял alpha.12, alpha.13, alpha.17 и alpha.18.
3. **Проходит ли полный релизный путь.** Прогнать `Release pipeline` на
репетиционном теге и предъявить, что подпись произведена, а
`Release Verification` её проверила на чистом хосте.
## Границы
Только `.github/workflows/linux-release.yml`, `deploy/**` и релизные тесты.
Не трогать `src/**`, консоль, Desktop и `agents/` кроме своих папок.
Не выпускать настоящий релиз и не двигать существующие теги.
Если по ходу выяснится, что для доказательства нужно закрепить версию
cosign явно (`cosign-release`) или перевести действие с тега на SHA — это
**предложение в отчёт**, а не самовольная правка: закрепление
cosign-installer тегом вместо SHA уже числится отдельным замечанием.
## Как проверить результат
- ссылки на прогоны `Release pipeline` и `Release Verification` с
репетиционным тегом и их фактический итог;
- **отдельно — проверка подписи потребителем на cosign v2.4.0**, как это
делает Windows-обновлятор. Release Verification гоняет только Linux-путь,
поэтому Windows-потребитель ею не покрыт;
- вывод шага, который печатает версию установленного cosign;
- вывод проверки подписи потребителем.
**Отдельное требование, из-за которого задание и появилось.** Репетиция
через `workflow_dispatch` идёт по `refs/heads/main`, поэтому шаги, закрытые
условием `refs/tags/*`, в ней не исполняются. Так был сожжён `alpha.19`:
репетиция была зелёной, а на теге упала сверка `DEFAULT_RELEASE_TAG`.
В отчёте требуется явно показать, какие шаги при репетиции исполнялись, а
какие пропущены — списком. Зелёная репетиция без этого списка
доказательством не считается.
## Контекст и ограничения
`docs/release-policy.md` фиксирует четыре поломки контракта
producer/consumer по cosign: alpha.12 (подпись без сертификата Fulcio),
alpha.13 (то же), alpha.17 (cosign v3 потребовал явный bundle или legacy
detached-output), alpha.18 (исправление негативного теста под detached-output).
Пятая поломка обойдётся ещё в один сожжённый номер версии.
Сожжённые номера — `alpha.10`, `alpha.11`, `alpha.19`; они не
переиспользуются.
Приемлемый результат задания — «обновление отклонено, причина такая-то».
Неприемлемый — «обновил, CI зелёный».

View file

@ -0,0 +1,63 @@
# Версия MSIX не менялась 14 релизов подряд
- **Кому:** `codex`
- **Дата:** 2026-08-20
- **От кого:** главный агент (`claude`)
- **Ветка:** `codex/msix-version`
- **Файл отчёта:** `../done/2026-08-20-msix-version-stuck.md`
## Что нужно сделать
`src/ServerMonitorManager.Desktop/Package.appxmanifest:13` содержит
`Version="1.0.0.6"`. Последний раз её меняли 2026-07-31 коммитом `c320b7d`
«chore(release): bump MSIX version to 1.0.0.6 (#9)» — то есть под
`v0.1.0-alpha.6`. С тех пор выпущено четырнадцать версий, вплоть до
`v0.1.0-alpha.20`, и в каждой из них MSIX объявляет себя как `1.0.0.6`.
Windows различает пакеты по версии в манифесте, а не по имени файла и не по
тегу GitHub. Установка нового MSIX поверх уже установленного с той же
версией не считается обновлением.
Нужно:
1. Определить фактическое поведение: устанавливается ли MSIX из alpha.20
поверх установленного MSIX из alpha.14, и что именно происходит.
Пока это **не определено** — я проверял чтением дерева, не установкой.
2. Если обновление действительно не проходит — связать версию пакета с
версией релиза, чтобы она поднималась при каждом выпуске автоматически,
а не руками.
3. Учесть, что `UpdateService` сравнивает версию манифеста с именем тега
релиза (`UpdateService.cs:256262`), а не с версией пакета. Значит
обновлятор может считать обновление доступным и скачать MSIX, который
Windows затем откажется ставить. Проверить эту связку целиком.
## Границы
`Package.appxmanifest`, скрипты сборки Windows-установщика
(`build/windows/**`) и, если потребуется, `windows-release.yml`. Не менять
логику `UpdateService` без отдельного основания: сначала измерение, потом
правка. Не трогать `src/**` вне Desktop, консоль и Linux-часть.
## Как проверить результат
- вывод `Get-AppxPackage` до и после установки: версия должна отличаться;
- показать установку MSIX новой версии поверх предыдущей на машине, где
предыдущая уже стоит, и её фактический результат;
- если решение автоматизирует поднятие версии — показать два прогона сборки
с разными тегами и разные версии в получившихся манифестах.
«Версия теперь поднимается» без установки поверх предыдущей результатом
проверки не является: проблема ровно в установке.
## Контекст и ограничения
Дефект найден чтением дерева и истории git, установка не выполнялась: на
машине разбора нет ни .NET SDK, ни установленного пакета. Если при проверке
окажется, что Windows нормально ставит MSIX той же версии поверх себя, —
так и напиши с выводом команд; это допустимый результат.
MSIX подписан тестовым сертификатом (`Publisher="CN=AppPublisher"`,
ассет `ServerMonitorManager-test-signing.cer`), поэтому для установки нужен
импорт этого сертификата в доверенные корневые. Постоянная доверенная
Windows code-signing identity в `docs/roadmap.md` числится невыполненной
(этап 6). Это отдельная задача, здесь её решать не нужно.

View file

@ -0,0 +1,52 @@
# Синхронизировать release status в README и переводах
- **Кому:** `codex`
- **Дата:** 2026-08-20
- **От кого:** главный агент (`claude`)
- **Ветка:** `codex/readme-release-status`
- **Файл отчёта:** `../done/2026-08-20-readme-release-status.md`
## Что нужно сделать
`README.md:101` утверждает, что текущий тестовый выпуск — `v0.1.0-alpha.14`.
Фактически опубликован `v0.1.0-alpha.20`. Те же строки статуса в
одиннадцати переводах `docs/i18n/README.*.md` рассинхронизированы между
собой и с корневым README.
Привести строку статуса релиза к `v0.1.0-alpha.20` в корневом README и во
всех переводах.
Задание адресовано тебе, а не выполнено мной, по прямому требованию
`docs/release-policy.md`: владелец релиза имеет единоличное право записи в
version sources, `deploy/**`, `tests/bootstrap/**`, релизные workflow, строку
статуса релиза в корневом README и в переводах. Остальные просят об
изменении в отчёте и не правят эти пути сами.
Это же закрывает пункт этапа 6 в `docs/roadmap.md`: «синхронизировать все
переводы README с текущим release status». Отметить его выполненным —
часть задания.
## Границы
Только строки статуса релиза в README и переводах плюс соответствующий
пункт в `docs/roadmap.md`. Не переписывать остальной текст переводов, не
трогать `CHANGELOG.md` и `docs/release-policy.md` — они уже приведены в
соответствие отдельной работой. Не менять `DEFAULT_RELEASE_TAG` и версии
сборки: релиз этим заданием не выпускается.
## Как проверить результат
- `grep -rn "alpha\." README.md docs/i18n/` — ни одной ссылки на версию
ниже `v0.1.0-alpha.20` в строках статуса; вывод команды в отчёт;
- перечислить в отчёте все двенадцать файлов с указанием, какая версия в
каждом стояла до правки. Если где-то стояла не `alpha.14`, а другая — это
само по себе результат, его надо показать;
- `bash tests/bootstrap/test-release-contract.sh` — зелёный, если он
затрагивает README;
- прогоны CI на PR — ссылками.
## Контекст и ограничения
Проверка выполнена мной чтением дерева, а не запуском сборки: на машине
разбора нет .NET SDK. Возможно, какой-то из переводов уже актуален —
я сверял только корневой README и не открывал все одиннадцать файлов.

View file

@ -21,5 +21,6 @@ Known release history:
- `v0.1.0-alpha.17` keeps checksum verification fail-closed while suppressing its success line, so pass-through commands such as `node-code` return only their machine-readable bootstrap output. Its automatic verification completed the clean-host Hub and Node installation, then the negative-test harness stopped while creating a wrong-identity signature because cosign v3 requires an explicit bundle or legacy detached-output mode. The immutable release and failed harness run remain as evidence; the negative test is corrected in the next version.
- `v0.1.0-alpha.18` makes the wrong-identity negative test select cosign v3's explicit detached-output mode, matching the production consumer's detached signature and certificate contract. It is the first release required to complete both automatic `workflow_run` verification and manual `workflow_dispatch` re-verification.
- `v0.1.0-alpha.19` exists as a tag, but its Release pipeline failed when the packaged convenience installer's `DEFAULT_RELEASE_TAG` did not match the immutable tag. No GitHub Release was published. The version number is burned and must not be moved, deleted, recreated, or reused.
- `v0.1.0-alpha.20` matches the packaged convenience installer's `DEFAULT_RELEASE_TAG` to the tag being produced, which is the defect that burned `v0.1.0-alpha.19`. It also adds a pipeline step that tests the latest published release before a new one is cut. It carries the content that `v0.1.0-alpha.19` failed to publish: the operator web console, the guided unified installer, the complete uninstall mode, the node enrollment endpoint, and the mesh state permission fix.
Every release candidate must pass a branch `workflow_dispatch` run of the Release pipeline before its immutable version tag is created. The release owner has sole write ownership of version sources, `deploy/**`, `tests/bootstrap/**`, release workflows, the root README release status, and translated README release statuses. Other contributors request changes to those paths in their report; they do not edit or bump them directly. One pull request covers one release topic and may merge only after required CI is green.

View file

@ -46,6 +46,11 @@
- [x] ограниченный offline buffer Agent и downsampling;
- [x] idempotency и replay protection;
- [x] SQLite schema version, retention и backup/restore Control DB + CA;
- [x] веб-консоль оператора на Control: список узлов, состояние агентов и
выпуск кода регистрации;
- [x] отдача ассетов консоли из встроенных ресурсов сборки, когда каталога
`wwwroot` нет на диске;
- [x] вход по логину и паролю для тестовых стендов, отключённый по умолчанию;
- [ ] добавить Desktop UI управления Automation identities и токенами.
## Этап 3 — управляемые Links
@ -62,6 +67,12 @@
- [x] B-2: независимая фоновая и emergency-triggered реконсиляция, агрегированное состояние недоступного Mesh firewall и Desktop banner;
- [x] B-3: факт-первичная реконсиляция использует один `link-list`, удаляет orphan/дубликаты (включая `Disabled/Disabled`), разводит `Examined` / `Converged` / `Failed` (M2), ограничивает marker-prompt тремя попытками, добавляет фильтр истории и retention завершённых Links (M4), а также типизированное ожидание активации Mesh Node (M5). Physical acceptance остаётся внешним блокером;
- [ ] выполнить физический acceptance Hub + source Node + два destination Node с WireGuard/nftables/reboot.
- [x] управление Links из веб-консоли: создание с подтверждением направления,
обязательной причиной и ключом идемпотентности;
- [x] аварийное отключение Link из строки таблицы консоли;
- [x] раздельное отображение желаемого и фактического состояния Link
с признаком рассогласования и последней ошибкой;
- [x] журнал событий Control в реальном времени в веб-консоли;
## Этап 4 — мониторинг и терминал
@ -101,6 +112,8 @@
- [x] добавить криптографическую подпись compatibility manifest для production release;
- [x] проверять Ubuntu 22.04/24.04 и Debian 12/13, `amd64`/`arm64`;
- [x] добавить non-interactive install/update/rollback/uninstall;
- [x] добавить режим полного удаления сервера;
- [x] добавить пошаговый объединённый установщик;
- [x] устанавливать Control/Agent, restricted helper и systemd units;
- [x] локально создавать Agent key/CSR, выполнять mTLS enrollment и не сохранять token;
- [x] добавить собственную установку WireGuard Hub/Node и выдачу внутренних адресов;