Окрема робота dbtest у ci.yml. Базу дає services: — докер-сокет усередину роботи не прокидається, тож кожна робота не отримує root на хості. Запобіжник імені бази спрацював на DSN роботи server і змусив завести окрему netpulse_probe: підлаштували конвеєр, а не запобіжник. Сторож вимагає в логу «застосовано міграцій: N» і «усе зелене проти бази» — «зелено, нічого не зробивши» неможливо. Задача 110 на раннері: success. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
45 KiB
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, гірша за її відсутність).
Обсяг: ~2–3 дні. Ризиків мало: схема готова, візерунок автентифікації в проєкті вже відпрацьований на агентських токенах.
Етап 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 — окрема задача, і базу для них варто
починати саме з імпортера, а не з ручного наповнення.
Обсяг: ~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 окремо.
Етап 11. Сім задач одним заходом — 2026-08-27
Міграції 0058–0064, зроблено паралельно. Розбір спільного знаменника — в 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
Міграції 0065–0068 плюс роботи без міграцій. Розбір спільного — у HISTORY.md, розділ «Друга сімка».
- 0065 трапи — приймач 162/udp на зонді, словник із шести протокольних трапів плюс словник кабінету, джерело алертів
trap.- 0066 ескалації — драбина сходинок, стан у базі, зупинка при підтвердженні, заглушення відкладає сходинку, а не витрачає.
- 0067 алерт про диск — пороги за часом (21 доба / 4 доби), а не за відсотками; вільне місце міряється
statfsпоBavail.- 0068 каталог профілів — поля заливки переїхали з разової міграції в
catalog.json.plural— 69 місць виклику, 13 друкували число двічі; підпис змінено так, щоб помилка стала неможливою.- Тести вебу — з нуля до 137; знайшли 11 справжніх вад, усі виправлені.
Увімкнути трапи — рішення власника
Код розгорнуто, модуль не увімкнено. Щоб запрацював, потрібні три речі, і третя виходить за межі технічної:
trapsу-modulesзонда;NET_BIND_SERVICE— процес не root, а 162 привілейований;- публікація 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» не перевіряє ніщо, а ламається найчастіше саме він: міграції поверх наявних даних, зміна конфігурації, несумісність зонда з новим колектором.
Потрібен другий режим пісочниці: підняти попередню версію, налити в неї даних, оновити до нової, і прогнати ту саму самоперевірку. Без цього перший же реліз перевіряє себе на клієнтові.
Сумісність зонда й сервера. Зонди стоять у мережах клієнтів і оновлюються не одночасно з сервером. Правило «який зонд працює з яким сервером» ніде не записане й не перевіряється.
Порядок і чому саме такий
Етап 5 (користувачі) — без входу продукт не можна віддати нікому.✅Етап 9 (алерти) — без сповіщень це не моніторинг.✅- Етап 6 (шаблони) — знімає потребу дописувати Go під кожен вендор; що раніше, то менше захардкодженого коду доведеться викидати.
Етап 8 (керування зондом) — дешевий і робить онбординг можливим.✅- Етап 7 (NCM) — головний аргумент Enterprise-тарифу.
- Етап 10 (мобільний) — після того, як є що показувати.
Дашборди з NOC TV-режимом зроблено 2026-08-25. Що лишається — нижче, окремим переліком із причинами.
Що лишається — стан на 2026-08-28
Перелік нижче (розділ «Чого немає») складено 2026-08-25 і частина його вже неправдива. Тут — те, що справді відкрите СЬОГОДНІ, у порядку ціни помилки. Нижчий розділ лишено як довший опис причин.
Перше: до першого тегу (Етап 13)
Це єдина річ, яка блокує продаж, і вона не про код.
- Оновлення з версії на версію не перевіряв ніхто. Пісочниця перевіряє установку з нуля. Шлях «стояла 0.1 — стала 0.2» ламається частіше, а перевіряє його зараз перший клієнт.
- Немає версіонування. Образ завжди
v0.1.0, реєстру немає, тега немає. Поки цього немає, «оновитись» означає «залити дерево й зібрати», тобто те, що роблю я вручну. - Сумісність зонда й сервера ніде не записана. Зонди стоять у чужих мережах і оновлюються не разом із сервером.
Друге: перевірки, які є, але не бігають самі
✅ 2026-08-28: окрема роботаscripts/dbtest.shне в CI.dbtestу.forgejo/workflows/ci.yml, базу даєservices:(докер- сокет усередину роботи не прокидається), сторож вимагає накату всіх міграцій з нуля — «зелено, нічого не зробивши» неможливо. Прогнано на раннері: задача 110, success. Заодно спростувалось те, що написано нижче в старому переліку: CI не «чекає на раннера», він працює весь день — на комітc83324aпройшли всі п'ять робіт.- Токен у
originзамість ключа розгортання.
Третє: діри, названі рецензіями й не закриті
LoadChannelsпадає цілком через один нерозшифровний секрет — кабінет лишається без ескалацій до стелі життя драбини.- Перетягування вузла на мапі не покрите тестом і не буде (d3-drag не запускається синтетичними подіями). Наслідок відомий: вузол стає на місце, запит не йде, тести зелені.
- SNMPv3-трапи не перевіряються — розбираються й зберігаються, підпис і шифрування не звіряються.
- Тригери шаблонів не можуть отримати драбину ескалації.
plural()повертає число разом із рядком, а частина викликів додає його ще раз: «5 5 хостів».
Четверте: ніколи не працювало на живому залізі
- Відкат конфігурації — код є, на справжньому обладнанні не виконувався жодного разу.
- Джерело тригера
trap— свідомо не зроблено без словника MIB. - Прототипи шаблонів (0059) увімкнені, але
snmp.ifчерез них не виражається.
П'яте: велике й відоме
- Білінг — схема з 0009 і 0069 є, продавати без нього не можна.
- Web Push, збережені подання, прокрутка дашбордів на TV, підкладки-плани приміщень, мобільний режим мапи.
- Справжній TLS — потрібен домен; на IP Let's Encrypt не видає.
Чого немає: повний перелік
Стан на 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% зусиль.