Netpulse_SasS/ROADMAP.md
byrsapty 08801f631c
All checks were successful
CI / hygiene (push) Successful in 9s
CI / web (push) Successful in 59s
CI / server (push) Successful in 1m13s
CI / agent (push) Successful in 2m54s
Стеля журналів, терплячіша самоперевірка, Етап 13 у плані
ЖУРНАЛИ БЕЗ СТЕЛІ. На запитання «чому не пишемо в /var/log» правильна
відповідь — «бо в контейнері це не той шар»: файл усередині зникає при
перестворенні, невидимий для docker logs і вимагає власної ротації.
Але за питанням стояла справжня вада: драйвер json-file був
налаштований порожньо, тобто НЕ КРУТИВ НІЧОГО. Проксі за добу набрав
27 МБ; до повного диска були місяці, і першим ліг би Postgres.

Тепер 10 МБ × 3 файли на службу — близько 250 МБ на інсталяцію, і
драйвер міняється однією змінною (journald, syslog) для тих, кому
потрібні справжні файли або чужий збирач.

САМОПЕРЕВІРКА ЗОНДА чекала хвилину й одного разу вже дала хибне
червоне: зонд зареєструвався, просто пізніше — перший старт припадає
на найзавантаженішу мить установки. Тепер три хвилини з повідомленням
кожні півхвилини: мовчазна пауза невідрізненна від зависання, а хибне
червоне після успішної установки коштує години пошуку неіснуючої
поломки.

GITSTORE: TestDeleteBranchLocal кличе зовнішній git і не мав перевірки
на його відсутність — падав у golang:1.25-alpine, тобто саме там, де
його проганяють. Сусідній тест таку перевірку має.

ЕТАП 13 у ROADMAP: реєстр образів, релізи, пакети. Записано, чому
порядок саме такий (пакет без реєстру ставив би «зберіть самі») і чого
треба досягти до першого тегу — оновлення з версії на версію не
перевіряв ніхто, а ламається найчастіше саме воно.
2026-08-27 21:52:27 +03:00

41 KiB
Raw Blame History

NetPulse — план робіт

Документ доповнює HISTORY.md: той описує зроблене, цей — що лишилось і чому саме в такому порядку.

Де ми зараз

Працює наскрізний ланцюг кабель → браузер: зонд знаходить сусідів по LLDP/CDP/ARP, сервер зводить їх у topo.links, сам заводить чеки на трафік, API віддає готове полотно з живими статусами, редактор пише назад із контролем конфліктів і відкатом.

Перевірено на живому стенді проти справжнього snmpd. Деталі — в README кожного компонента.

Чого немає: чесний зріз

Схема БД з Етапу 1 покриває майже все з технічного завдання, але схема ≠ реалізація. Нижче — розрив між ними великими мазками; поіменний перелік із причинами — у розділі «Чого немає: повний перелік» наприкінці.

Підсистема Схема Реалізація
Мапа, топологія, телеметрія
Автовиявлення LLDP/CDP/ARP/FDB
Користувачі, ролі, вхід
Шаблони опитування з тригерами, автопризначенням, snmp.walk і прототипами (0059)
NCM (збір конфігів) збір, розклад, Git, syslog, compliance і відкат (0060)
Керування зондом із 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, гірша за її відсутність).

Обсяг: ~23 дні. Ризиків мало: схема готова, візерунок автентифікації в проєкті вже відпрацьований на агентських токенах.


Етап 6. Шаблони опитування (Zabbix-подібні) — ⚠️ наполовину, 2026-08-24

Зроблено: схема tpl.*, реконсиляція шаблонів у core.checks, звірка планів зонда, REST і редактор у вебі, чотири вбудовані шаблони. Перевірено наскрізно на живому net-snmp.

Додано 2026-08-24: шаблон описує перевірки будь-якого типу (не лише OID), імпорт/експорт глобальний і поштучний, ручні інтервали.

Додано 2026-08-24: графіки описуються в шаблоні (шість видів, жорсткі межі осей), картка хоста на вкладках без інтервалів.

Додано 2026-08-25: тригери описуються в шаблоні й розгортаються в правила сповіщень; автопризначення за sysObjectID — пристрій сам каже, що він таке, і шаблон чіпляється без жодного натискання. За тим самим sysObjectID тепер підбирається й профіль збору конфігів (ncm.profile_auto_assign, уточнення за sysDescr для випадків, коли один OID покриває різні типи заліза). Щоб було з чого підбирати, хост зі SNMP-доступом сам отримує чек розпізнавання: полегшений topology.discover — три OID, без сусідів і без обходу ifTable.

Лишилось: прототипи шаблонів і 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 — окрема задача, і базу для них варто починати саме з імпортера, а не з ручного наповнення.

Обсяг: ~57 днів на схему + реконсиляцію + імпортер + 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 із двоетапним погодженням уже є).

Обсяг: ~45 днів.


Етап 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 уже наповнюється), повзунки лімітів, перемикачі модулів, кнопка «запустити виявлення», інструкція встановлення з готовою командою й токеном.

Обсяг: ~23 дні.


Етап 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 у схемі готова.

Обсяг: ~45 днів.


Етап 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усе з ТЗ.

Обсяг: ~34 дні на адаптив + PWA, Mini App окремо.


Етап 11. Сім задач одним заходом — 2026-08-27

Міграції 00580064, зроблено паралельно. Розбір спільного знаменника — в HISTORY.md, розділ «Сім задач одним заходом».

  • 0058 подієві алертиsyslog, ncm, compliance спрацьовують у мить надходження події; правило з нереалізованим джерелом більше не зберігається мовчки.
  • 0059 snmp.walk і прототипи — таблиці з динамічним індексом описуються шаблоном, а не Go.
  • 0060 відкат конфігу — план як різниця, маскування паролів із підписом плану, обов'язковий контрольний збір, verifying при обриві. MikroTik і Juniper відмовлені з поясненням.
  • 0061 кнопки Telegram — довге опитування (домену немає й не передбачається), авторизація не з callback_data, прив'язка акаунта одноразовим кодом.
  • 0062 аудит і архів хостів — вісім відсутніх назв дій; тест на AST, що падає на ключі без назви; «відновити» повертає хост робочим, а не мовчазним.
  • 0063 RLS — три ролі, окремий пул для фонових тактів. Інертна до перемикання DSN.
  • 0064 строки зберігання — три гіпертаблиці й дві звичайні таблиці, що росли назавжди; сторінка сховища з прогнозом.

Лишилось із цього етапу

  • Тест ізоляції RLS не прогнано. 2026-08-27: прогнано на бойовій базі, перемикання зроблено. Ізоляція діє, вхідні шляхи переведено на воркерний пул. Подробиці — HISTORY.md, розділ «Перехід на роль без BYPASSRLS». Лишилось: телеметрія, аудит та історія алертів під RLS не підпадають і не підпадуть — TimescaleDB не поєднує стиснення з row level security. Їхню ізоляцію далі тримає предикат у запиті.
  • 0064 не прогнано на живій БД. Перевірити першими: chunks_detailed_size над матеріалізованою гіпертаблицею, hypertable_compression_stats на нестиснутій, add_retention_policy всередині транзакції під SECURITY DEFINER.
  • Алерт про вичерпання диска — найдешевший шлях без правок движка: писати db.size.bytes і db.days_left звичайними метриками на хості машини зонда, тоді наявне метричне правило працює як є.
  • apply_* у генераторі профілів. Поля заливки задані міграцією через UPDATE; db/profiles/catalog.json про них не знає.
  • plural() повертає рядок разом із числом, а частина місць виклику додає число ще раз — на екрані «5 5 хостів». Стара вада, не з цього етапу.

Етап 12. Друга сімка — 2026-08-27

Міграції 00650068 плюс роботи без міграцій. Розбір спільного — у HISTORY.md, розділ «Друга сімка».

  • 0065 трапи — приймач 162/udp на зонді, словник із шести протокольних трапів плюс словник кабінету, джерело алертів trap.
  • 0066 ескалації — драбина сходинок, стан у базі, зупинка при підтвердженні, заглушення відкладає сходинку, а не витрачає.
  • 0067 алерт про диск — пороги за часом (21 доба / 4 доби), а не за відсотками; вільне місце міряється statfs по Bavail.
  • 0068 каталог профілів — поля заливки переїхали з разової міграції в catalog.json.
  • plural — 69 місць виклику, 13 друкували число двічі; підпис змінено так, щоб помилка стала неможливою.
  • Тести вебу — з нуля до 137; знайшли 11 справжніх вад, усі виправлені.

Увімкнути трапи — рішення власника

Код розгорнуто, модуль не увімкнено. Щоб запрацював, потрібні три речі, і третя виходить за межі технічної:

  1. traps у -modules зонда;
  2. NET_BIND_SERVICE — процес не root, а 162 привілейований;
  3. публікація 162/udp на хост — порт без автентифікації приймає будь-кого, хто знає адресу.

Обмеження в модулі є (20 трапів/с з адреси, стеля черги, окремий облік невідомих джерел), але вони зменшують шкоду, а не прибирають рішення.

Лишилось із цього етапу

  • db/profiles/build.py --check не в CI. Один рядок у кроці «Схема» ловив би розходження каталогу зі згенерованим — саме те, що цього разу знайшлось випадково.
  • CI без раннера. scripts/check.sh робить те саме однією командою вже сьогодні; сам workflow чекає на раннера.
  • Перетягування вузлів на мапі не покрите й не буде — d3-drag не запускається синтетичними подіями. Наслідок: вузол візуально стає на місце, запит не йде, розкладка «сама відкочується» після перезавантаження, і всі тести при цьому зелені.
  • Тригери шаблонів не можуть отримати драбину ескалації — поля в тригері шаблону немає, а драбина ще й тенант-специфічна.
  • SNMPv3-трапи не перевіряються — розбираються й зберігаються, підпис і шифрування не звіряються.

Етап 13. Випуск: реєстр образів, релізи, пакети — НЕ ЗРОБЛЕНО

Поставлена задача, дослівно: «ми ж зможемо його встановлювати з пакетів? А то зараз не дуже».

Зауваження справедливе. Сьогоднішній шлях клієнта — клонувати репозиторій, зібрати образи в себе (кілька хвилин і гігабайти) і запустити установник. Це шлях розробника: клієнт бачить вихідний код, якого не мав би бачити, і платить за збірку часом свого сервера.

Чому «пакет» неможливий просто зараз

Не тому, що ліньки написати .deb. Немає що в нього класти. Образи збираються на машині клієнта з джерел, тобто продукт як артефакт не існує — існує лише рецепт. Пакет у такому стані ставив би те саме «зберіть самі», лише з гарнішою обгорткою.

Тому порядок жорсткий: спершу реєстр, потім релізи, і лише потім пакети.

13.1 Реєстр образів

Інфраструктура вже є: Forgejo має вбудований реєстр контейнерів, а CI-раннер ми запустили 2026-08-27. Бракує лише того, щоб почати ним користуватись.

Образи netpulse/server і netpulse/agent мають публікуватися туди з тегом версії. Після цього docker compose на машині клієнта тягне готове, а не збирає.

13.2 Версіонування, якого немає

NETPULSE_VERSION=v0.1.0 — зараз просто рядок у .env. Тега в git немає, образу з таким тегом ніде, крім бойового стенду, немає теж. Зв'язку «версія → коміт → образ» не існує, тобто на питання «що саме у вас працює» відповіді немає.

Потрібен тег у git як єдина точка істини, і збірка, що бере версію звідти, а не з рядка в конфігурації.

13.3 Випускальний конвеєр

git tag v0.2.0  →  CI збирає  →  штовхає образи в реєстр
                              →  публікує netpulse-v0.2.0.tar.gz

В архіві — netpulse, compose-файли, netpulse.conf.example. Без джерел. Плюс install.sh, який цей архів завантажує:

curl -fsSL https://git.zotac.keenetic.link/.../install.sh | sh

І netpulse upgrade починає ходити в реєстр замість перезбірки.

13.4 Пакети .deb / .rpm — кроком пізніше

netpulse у /usr/bin, compose у /opt/netpulse, netpulse.conf у /etc/netpulse, systemd-юніт. Тоді працює те, чого й очікують:

apt install netpulse && netpulse install

Робити це до 13.1 безглуздо з причини, названої вище.

Чого треба досягти ДО першого тегу

Оновлення з версії на версію ніхто не перевіряв. Пісочниця установника (Етап 12) перевіряє установку з нуля — і саме вона знайшла дві вади, які чекали на першого клієнта. Але шляху «стояла 0.1, стала 0.2» не перевіряє ніщо, а ламається найчастіше саме він: міграції поверх наявних даних, зміна конфігурації, несумісність зонда з новим колектором.

Потрібен другий режим пісочниці: підняти попередню версію, налити в неї даних, оновити до нової, і прогнати ту саму самоперевірку. Без цього перший же реліз перевіряє себе на клієнтові.

Сумісність зонда й сервера. Зонди стоять у мережах клієнтів і оновлюються не одночасно з сервером. Правило «який зонд працює з яким сервером» ніде не записане й не перевіряється.

Порядок і чому саме такий

  1. Етап 5 (користувачі) — без входу продукт не можна віддати нікому.
  2. Етап 9 (алерти) — без сповіщень це не моніторинг.
  3. Етап 6 (шаблони) — знімає потребу дописувати Go під кожен вендор; що раніше, то менше захардкодженого коду доведеться викидати.
  4. Етап 8 (керування зондом) — дешевий і робить онбординг можливим.
  5. Етап 7 (NCM) — головний аргумент Enterprise-тарифу.
  6. Етап 10 (мобільний) — після того, як є що показувати.

Дашборди з NOC TV-режимом зроблено 2026-08-25. Що лишається — нижче, окремим переліком із причинами.

Чого немає: повний перелік

Стан на 2026-08-25, після розгортання на бойовому сервері. Перелік складений перевіркою коду, а не з пам'яті: для кожного пункту звірено, чи є під нього щось у server/internal, agent/internal і web/src.

Порядок — за тим, наскільки відсутність помітна тому, хто користується системою.

Схема є, коду немає зовсім

Ці таблиці створені міграціями й порожні. Небезпека тут не в самій відсутності, а в тому, що схема виглядає як обіцянка.

Що Де схема Чого бракує
Білінг і ліцензії 0009_billing_licensing.sql усього: тарифи, ліміти, Stripe, ключі. Для Micro-SaaS це те, через що продають
Web Push alr.push_subscriptions підписки й доставка. Потрібне для PWA
Звіти SLA core.sla_targets, core.sla_periods розрахунок доступності за період і вивантаження
Збережені подання core.saved_views фільтри інвентарю, які можна назвати й повернутись
Прокрутка дашбордів на TV dashboards.tv_options.rotate_sec сам режим NOC TV працює, але сторінки не гортаються по колу

Оголошено в переліку можливостей, не написано

Що Стан Чому не зроблено
Modbus-TCP плагін у сіді, is_core = false немає жодного інвертора чи UPS під рукою. Неперевірений промисловий протокол у мережі з живим обладнанням — гірше, ніж його відсутність
NetFlow / sFlow плагін у сіді, is_core = false окремий приймач потоків, за обсягом — власний етап

Обидва плагіни позначені is_core = false, тож у переліку перевірок система показує їх недоступними — обіцянки користувачу немає.

Зроблено наполовину

Сповіщення за подіями — лишився trap. syslog, ncm і compliance зроблено подієво (0058): правило перевіряється в мить надходження події. trap свідомо не реалізовано — без словника MIB умова звелась би до порівняння сирих OID, тобто до другої мовчазної обіцянки замість першої. Джерела link і agent не подієві за природою. Правило з нереалізованим джерелом тепер не зберігається, а не мовчить.

Підкладки-плани приміщень. topo.map_backgrounds віддається в GET /maps/{id}, полотно їх не малює. Потрібен прийом і роздача файлів — іконки мап уже зберігаються в базі, тож можна тим самим шляхом, без S3. Приблизно пів дня.

Мобільний режим мапи. Адаптив сторінок є, але на полотні перетягування вузла пальцем не розведене з панорамуванням: потрібен явний режим редагування, інакше кожна спроба посунути карту рухатиме вузол.

Інтерфейси досі захардкоджені. Прототипи шаблонів зроблено (0059), але snmp.if через них не виражається: зонд тримає попередній замір, рахує швидкість за фактичним інтервалом і ловить перевертання лічильника, а лічильники лягають у ts.if_counters за interface_id, а не в ts.samples за міткою. На цьому interface_id тримаються анімація трафіку на мапі, інспектор лінка й тригери з джерелом interface. Виграш — мінус ~200 рядків Go; ризик — обірвані графіки на живих хостах. Свідомо відкладено.

Перевірки, яких немає

Тести вебу з'явились 2026-08-27 — vitest, 137 перевірок, і scripts/check.sh проганяє обидва світи однією командою. Що покрито і, головне, що НІу web/TESTING.md. Найбільша діра лишається та сама: перетягування вузла на мапі не покрите, бо d3-drag не запускається синтетичними подіями; серверний бік цієї дії тестами покритий.

CI жодного разу не запускався. .forgejo/workflows/ci.yml написано, але раннера немає. Прогони робились руками; те, що CI мав би ловити (gofmt, go vet, тести з базою, крос-збірка зонда), проганялось окремо перед кожним комітом.

Чого не буде без зовнішніх умов

  • Справжній TLS на бойовому сервері — потрібен домен. Зараз самопідписаний: Let's Encrypt не видає сертифікатів на IP-адреси.
  • Telegram Mini App — окрема робота, і почати варто з приймача кнопок бота: він дає 90% користі за 10% зусиль.