Commit graph

9 commits

Author SHA1 Message Date
6de46550ea Пісочниця: пояснити нуль в аудиті й додати пробу, яка щось доводить
All checks were successful
CI / hygiene (push) Successful in 9s
CI / web (push) Successful in 1m4s
CI / server (push) Successful in 1m5s
CI / dbtest (push) Successful in 58s
CI / agent (push) Successful in 53s
«журнал аудиту 0 → 0 ok» читалось як доказ роботи журналу. Насправді
установник робить рівно три речі, і всі три — у переліку сліпих зон
(store.AuditBlindSpots): власник, строки зберігання, локальний зонд. Дані
пісочниця наливає прямим SQL, повз API. Нуль тут очікуваний.

Пробу лишено (покаже, якщо колись почнуть писати), але поруч тепер
сказано, що правило min на нулі не доводить нічого. І додано пробу, яка
доводить: спроби входу — самоперевірка заходить у продукт, порожня
таблиця означала б, що записування входів відвалилось.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:30:54 +03:00
3d5d1c6967 Пісочниця оновлення: перевірка перевіряла одну пробу з вісімнадцяти
Some checks failed
CI / hygiene (push) Successful in 8s
CI / web (push) Successful in 1m2s
CI / server (push) Successful in 1m13s
CI / dbtest (push) Successful in 58s
CI / agent (push) Has been cancelled
Перший прогін на стенді пройшов, але таблиця «дані до й після» мала
ОДИН рядок замість вісімнадцяти — і виглядала повною.

docker exec читає stdin, а stdin у циклі проб — той самий їх перелік.
Перша проба з'їдала решту, цикл завершувався після одного оберту, і
нічим цього не виказував: рядок є, «ok» є.

Одне </dev/null. Тепер видно, що міграція пройшла по базі з 6936
відліками телеметрії, 24 конфігами й 12 алертами, а не по порожній.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:27:10 +03:00
32442f8177 Рішення власника: дзеркало конфігів лишається дослівним
All checks were successful
CI / hygiene (push) Successful in 8s
CI / web (push) Successful in 1m4s
CI / server (push) Successful in 1m8s
CI / dbtest (push) Successful in 1m0s
CI / agent (push) Successful in 1m29s
Обрано побайтову точність архіву замість маскування — без неї
відновлення з архіву не працює. Наслідок прийнято свідомо: доступ до
дзеркала рівносильний доступу до всіх паролів у мережі.

Маскування лишається там, де конфіг читає людина: план відкату й
стенограми завдань.

Записано, щоб не переобговорювати.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 23:52:30 +03:00
5c9143cd98 Тести проти бази в CI: прогнано на раннері, success
Some checks failed
CI / web (push) Waiting to run
CI / server (push) Waiting to run
CI / dbtest (push) Waiting to run
CI / agent (push) Waiting to run
CI / hygiene (push) Has been cancelled
Окрема робота dbtest у ci.yml. Базу дає services: — докер-сокет
усередину роботи не прокидається, тож кожна робота не отримує root на
хості. Запобіжник імені бази спрацював на DSN роботи server і змусив
завести окрему netpulse_probe: підлаштували конвеєр, а не запобіжник.

Сторож вимагає в логу «застосовано міграцій: N» і «усе зелене проти
бази» — «зелено, нічого не зробивши» неможливо.

Задача 110 на раннері: success.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 23:48:54 +03:00
c83324ae3e Відкат на живому залізі: пароль адміністратора лежав відкритим
Some checks failed
CI / hygiene (push) Successful in 9s
CI / web (push) Successful in 1m13s
CI / server (push) Successful in 1m34s
CI / dbtest (push) Successful in 1m52s
CI / agent (push) Has been cancelled
Механізм відкату існував з 0035 і жодного разу не виконувався на
справжньому обладнанні. Прогін до межі заліза (план -> заявка ->
погодження, без заливки) дав три правильні відмови й одну знахідку.

D-Link питає пароль інтерактивно, і `show config` віддає відповіді
окремими рядками без ключових слів. Порядкове маскування за зразками
такий рядок не бачить, тож пароль адміністратора живого комутатора
лежав у плані відкату, у ncm.rollbacks і в git-дзеркалі — маскування
перед записом у git немає взагалі.

redactLines отримав стан: після рядка заведення облікового запису до
двох односкладових рядків маскуються. Ім'я запису лишається видимим.

Дорогою: перший рядок збереженого конфігу — відлуння команди
(`Command: show config`), і планувальникклав його в команди до заливки.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 23:42:43 +03:00
08801f631c Стеля журналів, терплячіша самоперевірка, Етап 13 у плані
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
ЖУРНАЛИ БЕЗ СТЕЛІ. На запитання «чому не пишемо в /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
ca143a616b Білінг, SLA, вбудовані правила, пісочниця установника, тести сторінок
Some checks failed
CI / hygiene (push) Successful in 8s
CI / web (push) Successful in 59s
CI / server (push) Failing after 3m27s
CI / agent (push) Successful in 3m3s
П'ять паралельних задач. Найцінніше в них — не можливості, а знайдене.

0069 БІЛІНГ. Аудит 0009 показав, що перевірка ліміту не спрацювала б
жодного разу: isPlanLimit шукала слово «ліміт», а тригер писав
"device limit reached" англійською. Перше ж досягнення стелі дало б
клієнту 500 замість пояснення. Плюс три діри: тригер лише на INSERT
(стеля в 15 обходилась за чотири дії через архів), max_maps/max_agents/
max_users не перевіряло ніщо — тобто рівно те, чим відрізняються плани,
і license_keys була закрита політикою tenant_isolation з 0011, хоча
tenant_id там NULLABLE навмисно: головний сценарій self-hosted був
недосяжний.

Після закінчення ліцензії не вимикається нічого — замерзає лише ріст.
Моніторинг, що перестав моніторити через несплачений рахунок, це
аварія в мережі клієнта, спричинена нами.

0070 SLA. Джерелом обрано ts.icmp_1h, а не device_status_history:
остання не вміє сказати «ми не знали» — перехід пишеться лише при
зміні стану, тож доба мовчання зонда виглядає як доба роботи. Час
розкладено на чотири частини, і «немає даних» не додається ні до чого;
замість вибору між двома брехнями звіт каже, яку частку періоду він
бачив. Закритий період тримає тригер, а не домовленість у Go.

0071 ВІДПОВІДНІСТЬ. 20 правил, кожне прив'язане до родини: об'єднаний
вираз, що покриває Cisco й не покриває MikroTik, дав би «0 порушень» і
сховав сліпу пляму. Вендор не входить у перелік, доки для нього немає
зразка конфігу в тесті. TestBuiltinRulesAreNotAlwaysGreen вимагає, щоб
у кожного правила був конфіг, де воно спрацювало, І де ні.

ПІСОЧНИЦЯ УСТАНОВНИКА — та сама установка в ізольованому проєкті
compose. Знайшла дві справжні вади з трьох спроб:
  * healthcheck бази ходив unix-сокетом, а споживачі по TCP. При
    первинній ініціалізації Postgres слухає лише сокет — compose
    вважав базу здоровою, migrate отримував connection refused. На
    створеній базі цієї фази немає, тож вада чекала на першого клієнта;
  * у білому переліку модулів API не було traps і filecfg — зонд із
    приймачем трапів неможливо було зареєструвати взагалі.

ТЕСТИ СТОРІНОК: 137 → 252. Мережевий шар, права доступу, незворотні
дії, фільтри з адресного рядка. Підмінюється лише fetch і WebSocket —
api/client.ts працює справжній.
2026-08-27 21:17:23 +03:00
3f6bcbc5a0 Установник: підказка про зайнятий 9443 більше не шле шукати nginx
Перевірка портів давала на всі три випадки один текст: «майже завжди
сторонній веб-сервер (nginx, apache)». Для 80 і 443 це правда, для
9443 — ні: це порт колектора зондів, і найімовірніша причина там —
інший NetPulse на тій самій машині.

Знайшлось сухим прогоном на стенді, де бойова система тримає всі три
порти. Формально повідомлення «правильне» — порт справді зайнятий, — і
саме тому воно небезпечне: людина піде шукати nginx, якого немає.

Заодно зафіксовано, що сухий прогін працює: 12 кроків, обчислені
значення (shared_buffers 1024MB, стеля кешу 512mb, вісім фонових
робітників на чотирьох ядрах), і чесне «на справжній установці це
зупинило б» замість тихого продовження.
2026-08-27 17:43:05 +03:00
ed8fc831bf Дві сесії роботи: 0058–0068, розгортання однією командою, тести
Один коміт, а не десяток тематичних, свідомо: теми переплетені в
спільних файлах (store.go, docker-compose.yml, deploy/README.md), і
розділити їх можна було б лише індексуванням шматків. Коміти, які не
збираються, гірші за один великий — тим паче що це рівно той стан, який
перевірявся разом.

ЩО ПРАЦЮЄ НА СТЕНДІ Й ПЕРЕВІРЕНО ТАМ

  0058  подієві алерти: syslog, ncm, compliance спрацьовують у мить
        події; правило з нереалізованим джерелом більше не зберігається
        мовчки
  0059  snmp.walk і прототипи шаблонів — таблиці з динамічним індексом
        описуються шаблоном, а не Go
  0060  відкат конфігу: план як різниця, маскування паролів із підписом
        плану, обов'язковий контрольний збір, verifying при обриві
  0061  кнопки Telegram: довге опитування, авторизація не з callback_data
  0062  аудит і архів хостів; тест на AST, що падає на ключі без назви
  0063  RLS: три ролі, окремий пул для фонових тактів
  0064  строки зберігання даних і сторінка сховища
  0065  приймач SNMP-трапів; перевірено справжніми пакетами по дроту,
        переклад v1→v2 за RFC 3584 дає правильний OID
  0066  ескалації сповіщень
  0067  алерт про вичерпання диска
  0068  поля заливки конфігу переїхали в каталог профілів

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

ЩО ЩЕ НЕ ЗАПУСКАЛОСЬ

  netpulse            установник: одна команда замість 18 змінних і
                      593 рядків інструкції
  RLS з першого запуску  нова інсталяція під політиками одразу;
                      RLS-EXISTING-INSTALL.md лишається тільки для
                      старих інсталяцій
  .forgejo + CI       раннер не зареєстрований

Ці три перевірені компіляцією й міркуванням, але не виконанням.

ГОЛОВНИЙ ВИСНОВОК ДВОХ СЕСІЙ

Зелена перевірка доводить рівно те, що вона перевіряє. Тест ізоляції RLS
був правильний і зелений — і пропустив зламаний вхід, бо перевіряв «чи
не видно чужого», коли зламалось «чи видно своє». Інтеграційні тести
grpcapi були зелені, бо не виконувались. Схема, довідник і протокол
описували те, чого в коді не існувало, і виглядало це як готове.

Тому в кожному завданні цих сесій стояла вимога назвати НЕПОКРИТЕ, а
чотири задачі закінчились не можливістю, а відмовою: правило з
нереалізованим джерелом не зберігається, профіль без команд заливки
каже про це замість мовчазної кнопки, міграція RLS валить сама себе на
таблиці без політики, тест словника аудиту падає на ключі без назви.

Подробиці — HISTORY.md, розділи за 26 і 27 серпня.
2026-08-27 17:32:49 +03:00