Плагін http стояв у сіді як базовий — тобто обіцяний усім одразу, — а модуля не існувало. Тепер є: код відповіді, час, збіг слова в тілі й залишок днів до кінця сертифіката. Недоступність повертається нулем, а не помилкою чека: на графіку це читається як провал, і саме за цим ставлять тригер. Довіру до ланцюга сертифікатів свідомо не перевіряємо — питають строк, а самопідписаний теж має дату. Повторна лінія між вузлами й повторно доданий хост давали однакове «такий запис уже існує». Тепер кожен випадок каже, що робити далі. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
23 KiB
NetPulse — план робіт
Документ доповнює HISTORY.md: той описує зроблене, цей — що лишилось і чому саме в такому порядку.
Де ми зараз
Працює наскрізний ланцюг кабель → браузер: зонд знаходить сусідів по
LLDP/CDP/ARP, сервер зводить їх у topo.links, сам заводить чеки на трафік,
API віддає готове полотно з живими статусами, редактор пише назад із контролем
конфліктів і відкатом.
Перевірено на живому стенді проти справжнього snmpd. Деталі — в README кожного
компонента.
Чого немає: чесний зріз
Схема БД з Етапу 1 покриває майже все з технічного завдання, але схема ≠ реалізація. Нижче — розрив між ними.
| Підсистема | Схема | Реалізація |
|---|---|---|
| Мапа, топологія, телеметрія | ✅ | ✅ |
| Автовиявлення LLDP/CDP/ARP/FDB | ✅ | ✅ |
| Користувачі, ролі, вхід | ✅ | ✅ |
| Шаблони опитування | ✅ | ⚠️ є, з тригерами й автопризначенням; немає snmp.walk і прототипів |
| NCM (збір конфігів) | ✅ | ⚠️ збір, розклад, Git, syslog і compliance є; немає відкату |
| Керування зондом із UI | ✅ | ✅ |
| Алерти й сповіщення | ✅ | ✅ |
| Мобільна адаптивність, PWA | — | ⚠️ адаптив є, PWA немає |
| Веб-інтерфейс (навігація, сторінки) | — | ✅ |
| Дашборди, NOC TV | ✅ | ✅ |
| Білінг, ліцензії | ✅ | ❌ |
| Пакування, розгортання | — | ✅ зібрано й розгорнуто на живому сервері |
Етап 5. Веб із користувачами і правами — ✅ зроблено 2026-08-15
Реалізовано: вхід, JWT + ротація refresh-сесій, RBAC на всіх ендпоїнтах,
netpulse-userдля першого власника, сторінка входу, приховування дій без права, мобільний адаптив. Подробиці — HISTORY.md, контракт — server/API.md.Доповнено 2026-08-15: повноцінний веб — навігація й вісім сторінок (мапа, пристрої, алерти, правила, канали, зонди, команда, профіль), примітиви UI з мобільними картками замість таблиць, захист маршрутів правами.
Відкладено з цього етапу: TOTP (
totp_secret_encє в схемі, коду немає), запрошення поштою (core.invitationsпорожня — користувача заводять із паролем одразу), кастомні ролі (POST /api/v1/roles), фільтр заmemberships.scope_group_ids, редагування умови правила (лише створення й вимкнення), маршрути сповіщень і вікна обслуговування в UI.
Навіщо перше. Зараз API автентифікує лише машинні токени
(core.api_tokens). Людина увійти не може, а core.roles / permissions /
memberships лежать порожні. Без цього не можна ані впустити клієнта, ані
розмежувати доступ між інженером і глядачем — тобто продукт неможливо продати
навіть одній команді.
Сервер
POST /api/v1/auth/login email + пароль → access (JWT, 15 хв) + refresh
POST /api/v1/auth/refresh обмін refresh-токена
POST /api/v1/auth/logout відкликання сесії
GET /api/v1/me профіль + ефективні права
POST /api/v1/auth/totp/enroll двофакторка (поле totp_secret_enc уже є)
GET /api/v1/users керування командою
POST /api/v1/users/invite запрошення (core.invitations уже є)
PATCH /api/v1/users/{id} зміна ролі, скоупу груп
GET /api/v1/roles системні + кастомні (Enterprise)
POST /api/v1/roles кастомна роль із набором прав
Паролі — argon2id, не bcrypt: він стійкіший до GPU-перебору, а password_hash
у схемі вже text і формат не диктує.
Refresh-токен зберігається як sha256 у core.sessions — так само, як
агентські. Витік дампа БД не дає жодної живої сесії.
RBAC замінює перевірку scopes. Зараз обробники питають tok.Can("maps:write").
Стане: посередник резолвить memberships → roles → role_permissions у набір прав
і кладе в контекст; машинні токени лишаються, але їхні scopes перетинаються з
правами власника. Скоуп груп (memberships.scope_group_ids) фільтрує вибірки
пристроїв — це вже не middleware, а предикат у запитах store.
Фронтенд
Сторінка входу, зберігання refresh у httpOnly-cookie, сторінки «Команда» й «Ролі», приховування дій без права (кнопка, яка завжди дає 403, гірша за її відсутність).
Обсяг: ~2–3 дні. Ризиків мало: схема готова, візерунок автентифікації в проєкті вже відпрацьований на агентських токенах.
Етап 6. Шаблони опитування (Zabbix-подібні) — ⚠️ наполовину, 2026-08-24
Зроблено: схема
tpl.*, реконсиляція шаблонів уcore.checks, звірка планів зонда, REST і редактор у вебі, чотири вбудовані шаблони. Перевірено наскрізно на живому net-snmp.Додано 2026-08-24: шаблон описує перевірки будь-якого типу (не лише OID), імпорт/експорт глобальний і поштучний, ручні інтервали.
Додано 2026-08-24: графіки описуються в шаблоні (шість видів, жорсткі межі осей), картка хоста на вкладках без інтервалів.
Додано 2026-08-25: тригери описуються в шаблоні й розгортаються в правила сповіщень; автопризначення за
sysObjectID— пристрій сам каже, що він таке, і шаблон чіпляється без жодного натискання.Лишилось: прототипи шаблонів і
snmp.walkяк тип елемента (таблиці з динамічним індексом).
Етап 6 (початковий план)
Найбільша нова підсистема. Зараз snmp.if-чеки народжує захардкоджений
autochecks.go. Це працює рівно для одного випадку — інтерфейсів. Щойно
знадобиться CPU Cisco, температура MikroTik чи ємність UPS, доведеться дописувати
Go-код під кожен вендор. Шаблони роблять це даними.
Нова схема: tpl
tpl.templates -- key, name, vendor, is_builtin, tenant_id NULL = вбудований
tpl.template_links -- успадкування шаблон → шаблон
tpl.macros -- {$SNMP_COMMUNITY}, три рівні: глобальний → шаблон → пристрій
tpl.items -- один OID → одна метрика: key, check_type, params, interval,
-- units, value_type, preprocessing, metric_key
tpl.discovery_rules -- LLD: walk по таблиці (ifTable, entPhysicalTable, dot1dBase)
tpl.item_prototypes -- прототипи з {#IFNAME}, {#SNMPINDEX}
tpl.trigger_prototypes -- пороги, що народжують alr.rules
inv.device_templates -- прив'язка шаблон → пристрій
inv.group_templates -- прив'язка шаблон → динамічна група
Рішення, які треба зафіксувати одразу
Шаблон — декларація, core.checks — матеріалізація. Агент не має знати про
шаблони взагалі: він і далі отримує плаский план задач. Сервер реконсилює
шаблони × пристрої × LLD → core.checks при кожній зміні шаблону, складу групи
або результату виявлення. Це зберігає контракт агента незмінним і дозволяє
міняти шаблони без оновлення зондів у полі.
Low-level discovery — це той самий snmp.walk, результат якого йде не в
метрики, а в реконсиляцію. Тобто autochecks.go узагальнюється: замість
«знайшов інтерфейси → створив snmp.if» стане «правило виявлення повернуло
рядки → застосував прототипи → створив items».
Препроцесинг ділиться між агентом і сервером. Дельта лічильника (change per second) лишається на агенті: лише він знає фактичний інтервал між двома
опитуваннями — це вже реалізовано й перевірено. Решта (множник, регулярка,
JSONPath, discard unchanged) — на сервері при записі: інакше кожна зміна
правила вимагала б оновлення агентів.
Макроси розшифровуються на сервері й доїжджають до агента вже підставленими,
разом із креденшелами. Секретні макроси ({$SNMP_COMMUNITY}) лягають у
core.secrets тим самим механізмом, що й паролі.
Готова база шаблонів
Формат tpl.* варто спроєктувати так, щоб він приймав Zabbix-експорт
(YAML/JSON з items, discovery rules, prototypes, macros): його структура майже
один-до-одного лягає на запропоновану схему, а публічних шаблонів там сотні.
Це знімає потребу набивати базу вручну.
Команди збору конфігу вже є власним каталогом — db/profiles/catalog.json,
147 платформ. Шаблони опитування SNMP — окрема задача, і базу для них варто
починати саме з імпортера, а не з ручного наповнення.
Обсяг: ~5–7 днів на схему + реконсиляцію + імпортер + UI редактора шаблонів.
Етап 7. NCM — збір конфігів до кінця — ✅ зроблено 2026-08-25
Зроблено: агентський модуль SSH/Telnet, черга завдань, диспетчер, вивантаження зі стисненням і дедуплікацією, візуальний diff у вебі, планувальник за cron, Git-двигун на go-git, порівняння з довільною версією в UI, переливання історії командою
netpulse-gitsync. Перевірено наскрізно.Додано 2026-08-25: приймач syslog на зонді (RFC3164/5424, черга, ліміт частоти на джерело), транспорт
StreamLogs, зіставлення хоста за адресою джерела й тригер позачергового бекапу за зразком уncm.device_policies.syslog_match.
Етап 7 (початковий план)
Половина шляху вже є: ncm.* у схемі, ConfigJob/ConfigUpload у контракті,
сервер приймає чанки, звіряє sha256, дедуплікує за content_hash і шифрує тіло.
Бракує трьох частин.
Модуль ncm на агенті. SSH через golang.org/x/crypto/ssh, Telnet своїм
кодом (протокол тривіальний). Виконує команди з профілю, ловить prompt за
регуляркою, віддає сирий текст чанками. Для bdcom-olt і mikrotik-routeros
профілі вже в сіді — вони й стануть першими підопічними.
Планувальник на сервері. ✅ Cron із ncm.device_policies.cron — власний
парсер internal/cronx, тік раз на хвилину під advisory-блокуванням. Лишився
тригер за Syslog-подією (on_syslog, %SYS-5-CONFIG_I) — поле в схемі є,
читати його нікому.
Git-двигун. ✅ go-git, чистий Go без cgo. Голий репозиторій на тенанта,
гілка refs/heads/device/<device_id> на пристрій, файл <пристрій>/<тип>.cfg.
Однаковий вміст нового коміту не створює. netpulse-gitsync переливає вже
накопичену історію й відтворює втрачений репозиторій із бази.
UI: ✅ список версій і порівняння з довільною попередньою. Кнопка відкату
лишилась (сутність ncm.rollbacks із двоетапним погодженням уже є).
Обсяг: ~4–5 днів.
Етап 8. Керування зондом із UI — ✅ зроблено 2026-08-25
Зроблено:
EnrollmentServiceіз одноразовими запрошеннями, файл посвідчення на агенті, сторінка зондів із командою встановлення, керування модулями й лімітами, видалення зонда. Перевірено наскрізно.Лишилось: справжній CA з підписом CSR — контракт це передбачає, але власний центр сертифікації з ротацією є окремою системою.
Етап 8 (початковий план)
Транспорт готовий повністю: сервер уже вміє штовхати ModuleControl (які модулі
вмикати), TaskDelta (що опитувати), ліміти в Welcome (паралельність, розмір
батчу, темп ICMP) і Directive (пауза, перезавантаження конфігу, оновлення).
Бракує того, що це все вмикає.
POST /api/v1/agents створити зонд + одноразовий enrollment-токен
PATCH /api/v1/agents/{id} ліміти, дозволені модулі, сайт
POST /api/v1/agents/{id}/discover запустити автовиявлення зараз (TriggerNow уже є)
DELETE /api/v1/agents/{id}
EnrollmentService — реалізувати серверну сторону: зонд приходить із
одноразовим токеном і CSR, іде з підписаним сертифікатом. Контракт написано ще на
Етапі 2, реалізації немає. Без цього зонди заводяться INSERTом, що прийнятно на
стенді й неприйнятно у клієнта.
UI: сторінка зонда з самометриками (ts.agent_health уже наповнюється),
повзунки лімітів, перемикачі модулів, кнопка «запустити виявлення», інструкція
встановлення з готовою командою й токеном.
Обсяг: ~2–3 дні.
Етап 9. Алерти й сповіщення — ✅ зроблено 2026-08-15
Реалізовано: движок правил (icmp/interface/metric/no_data), антифлап вікном, кореляція за топологією, вікна обслуговування й ручне заглушення, маршрути з тихими годинами, доставка в Telegram/webhook/ SMTP, панель алертів у UI. Подробиці — HISTORY.md, контракт — server/API.md.
Відкладено з цього етапу: ескалації й повторні сповіщення, приймач кнопок Telegram (
callback_data), Web Push (іде з Етапом 10), правила з джерелsyslog/trap/ncm/compliance.
Те, без чого це не моніторинг. Система малює мапу, але мовчить, коли щось
падає. Схема готова з Етапу 1 (alr.rules, alerts, channels, routes,
escalation_policies, maintenance_windows, mutes) і повністю порожня.
Дані вже течуть: зміни статусу йдуть через core.event_outbox, метрики лежать у
TSDB, пороги описані в map_edges.thresholds.
Движок правил читає ts.icmp_samples, ts.if_counters і ts.samples_5m,
застосовує for_seconds (антифлап) і пише в alr.alerts. Дедуплікацію вже
гарантує унікальний індекс alerts_active_dedup_uniq.
Кореляція за топологією — та, заради якої будувалась topo.links: коли падає
маршрутизатор, 40 пристроїв за ним не мають дати 40 сповіщень. Поля
root_alert_id і depends_on_topology для цього вже є.
Канали: Telegram (бот + Mini App із кнопками Ack/Mute), Web Push, email,
webhook. alr.push_subscriptions у схемі готова.
Обсяг: ~4–5 днів.
Етап 10. Мобільна адаптивність і PWA
Зараз UI розрахований лише на десктоп: фіксований сайдбар 240 px, шапка з десятком елементів в один рядок, жодного брейкпойнта, полотно без тач-жестів.
Адаптив: сайдбар у висувну панель під md, шапка в дві смуги, цілі дотику не
менше 44 px, інспектор вузла — нижнім аркушем замість бічної колонки.
Полотно на дотик: React Flow вміє pinch-zoom і pan, але перетягування вузла пальцем конфліктує з панорамуванням — потрібен явний режим «редагування», інакше кожна спроба посунути карту рухатиме вузол.
PWA: manifest, service worker (кеш оболонки, не даних — застаріла мапа гірша
за її відсутність), Web Push через alr.push_subscriptions.
Telegram Mini App: перегляд мапи й алертів, кнопки Ack / Mute / View Diff — усе з ТЗ.
Обсяг: ~3–4 дні на адаптив + PWA, Mini App окремо.
Порядок і чому саме такий
Етап 5 (користувачі) — без входу продукт не можна віддати нікому.✅Етап 9 (алерти) — без сповіщень це не моніторинг.✅- Етап 6 (шаблони) — знімає потребу дописувати Go під кожен вендор; що раніше, то менше захардкодженого коду доведеться викидати.
Етап 8 (керування зондом) — дешевий і робить онбординг можливим.✅- Етап 7 (NCM) — головний аргумент Enterprise-тарифу.
- Етап 10 (мобільний) — після того, як є що показувати.
Далі за ТЗ лишаються дашборди з NOC TV-режимом, білінг зі Stripe і ліцензійні ключі — усі три мають готову схему й жодного коду.
Дрібний борг
- Перетягування вузла через UI не покрите автотестом: синтетичні події не запускають d3-drag у React Flow. Шлях перевірений тестами сервера.
- Підкладки-плани приміщень віддаються API, але полотно їх не малює — потрібен прийом і роздача файлів (S3/MinIO або локальний диск).
modbusіnetflowоголошені в сіді плагінів і не написані. Обидва позначеніis_core = false, тож у переліку перевірок система показує їх недоступними — обіцянки немає. Писати Modbus без жодного інвертора під рукою означало б віддати неперевірений промисловий протокол.