惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
人人都是产品经理
人人都是产品经理
IT之家
IT之家
Cyberwarzone
Cyberwarzone
T
Troy Hunt's Blog
有赞技术团队
有赞技术团队
阮一峰的网络日志
阮一峰的网络日志
T
Threat Research - Cisco Blogs
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
G
GRAHAM CLULEY
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 叶小钗
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
The Hacker News
The Hacker News
Jina AI
Jina AI
T
Tor Project blog
V
Vulnerabilities – Threatpost
酷 壳 – CoolShell
酷 壳 – CoolShell
Spread Privacy
Spread Privacy
博客园_首页
C
Cybersecurity and Infrastructure Security Agency CISA
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Simon Willison's Weblog
Simon Willison's Weblog
Security Latest
Security Latest
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 司徒正美
V2EX - 技术
V2EX - 技术
I
Intezer
The Cloudflare Blog
Cisco Talos Blog
Cisco Talos Blog
SecWiki News
SecWiki News
博客园 - 【当耐特】
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
Lohrmann on Cybersecurity
Scott Helme
Scott Helme
Google Online Security Blog
Google Online Security Blog
量子位
The Last Watchdog
The Last Watchdog
AI
AI
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Security Affairs
P
Palo Alto Networks Blog
S
Secure Thoughts
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Attack and Defense Labs
Attack and Defense Labs

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как мы построили распределённый мониторинг аптайма
Михаил Волченков · 2026-06-19 · via Все публикации подряд на Хабре

Как мы построили распределённый мониторинг аптайма

Средний

11 мин

600

В прошлый раз я писал про рекурсивную задачу мониторинга: кто мониторит монитор? Если Prometheus упал — вы не видите ничего, и самое коварное тут в том, что отвалившийся мониторинг внешне неотличим от идеальной стабильности. Та статья заканчивалась честно и немного грустно: чистого решения нет, есть только слои подстраховки и остаточный риск, с которым приходится жить.

Эта статья — про то, что было дальше. Я попробовал зайти с другой стороны: не «как защитить монитор от падения», а «как сделать так, чтобы падать было нечему». Получился LibrePing — децентрализованный peer-to-peer мониторинг аптайма, где нет центрального сервера, который мог бы упасть и утащить за собой всё.

Сразу оговорюсь: это не серебряная пуля и не «убийца» Pingdom с UptimeRobot. Это попытка решить вполне конкретную архитектурную боль. Дальше расскажу, как оно устроено, без занудства про каждую структуру данных, но с достаточным количеством деталей, чтобы было понятно, где тут инженерия, а где маркетинг (его тут нет).

Если хочется сначала просто потыкать — вот живой публичный хаб, без регистрации: nl.lp.mw.gg (другие — в вики проекта).

С чего всё началось

У классического мониторинга две родовые проблемы.

Первая — это та самая рекурсия из прошлой статьи. Любая централизованная система мониторинга сама является инфраструктурой, а значит, тоже падает. Вы навешиваете на неё meta-heartbeat, dead man’s switch, кросс-мониторинг — и в какой-то момент ловите себя на том, что строите мониторинг для мониторинга мониторинга.

Вторая — точка обзора. Когда вы мониторите сервис из одного места (даже из «облака»), вы видите доступность из этого одного места. А «сайт лежит» и «сайт не открывается из вашего ДЦ в Франкфурте, но прекрасно открывается из Сингапура» — это очень разные инциденты, и отличить их из одной точки невозможно. Коммерческие сервисы решают это, держа сеть собственных точек присутствия. Это дорого, поэтому за это берут деньги.

Мне хотелось вещь, у которой обе проблемы решаются одной и той же архитектурой. Идея простая до неприличия: что если точки мониторинга — это и есть сама система? Не один сервер с пачкой воркеров, а сеть равноправных узлов, которые общаются напрямую друг с другом. Узел упал — сеть этого даже не заметила. Точек обзора ровно столько, сколько людей подняли узлы.

Это, по сути, прямой ответ на финал прошлой статьи. Там лучшим вариантом был «кросс-мониторинг: независимые системы следят друг за другом». LibrePing доводит эту мысль до логического предела — вся система и есть кросс-мониторинг, по построению.

Два типа узлов

В сети LibrePing живут узлы двух ролей.

Probe (зонд) — лёгкий агент. Он запускает проверки из своей точки: HTTP, TCP, DNS, TLS, ICMP, traceroute. Каждый результат он подписывает своим ключом и отправляет на хаб. Probe не требует белого IP, не хранит ничего долговременно и спокойно живёт на NAS дома или на копеечном VPS. И — важная деталь, к которой я вернусь ниже — он не привязан к одному-единственному хабу намертво.

Hub (хаб) — публично доступный узел. Он принимает результаты от зондов, проверяет подписи, хранит данные, отдаёт дашборд и — главное — разносит результаты другим хабам по mesh-сети на libp2p (это сетевой стек, на котором стоит IPFS). Центрального сервера нет: хабы федерируются напрямую между собой, как почтовые серверы или узлы Mastodon.

А ещё есть третья «роль», которая на самом деле вообще не сервер. Обычному пользователю поднимать ничего не надо: веб-дашборд прямо в браузере генерирует ключ Ed25519 — это и есть «аккаунт». Никакой регистрации, почты, пароля. Ваши мониторы и алерты подписаны этим ключом и расходятся по сети. Закрыли вкладку, открыли на другом устройстве с тем же ключом — увидели свои сервисы. Аккаунт — это просто пара ключей, которая живёт у вас, а не в чьей-то базе.

Как результат проверки путешествует по сети

Давайте проследим за одним пингом от рождения до дашборда — на этом пути спрятана вся суть.

  1. Probe запускает проверку, скажем, HTTP-запрос к вашему сайту. Получает результат: код 200, задержка 143 мс.

  2. Probe упаковывает это в структуру и подписывает своим приватным ключом. Подпись неотделима от данных.

  3. Probe отправляет подписанный результат на свой домашний хаб по обычному HTTPS.

  4. Хаб проверяет подпись, применяет политику доверия, сохраняет результат — и пересылает его дальше, соседним хабам по mesh.

  5. Соседний хаб получает результат через gossip и делает ровно ту же проверку независимо. Он не верит хабу-ретранслятору ни на грамм. Он верит только исходной подписи зонда — а публичный ключ зонда едет внутри самого результата.

Вот этот пятый пункт — ключевой. В сети нет «доверенных» узлов. Хаб, который переслал вам данные, мог быть скомпрометирован, мог быть откровенно вредоносным — неважно. Если подпись зонда не сходится, результат летит в мусор. Доверие привязано к ключу автора данных, а не к каналу доставки. Это и называется self-certifying identity: ID узла — это хэш его публичного ключа, подделать ID, не имея ключа, нельзя.

Gossip — штука best-effort, какие-то сообщения теряются. Поэтому свежеподключившийся хаб ещё и дозапрашивает у соседей то, что пропустил, через отдельный поток синхронизации — и снова перепроверяет каждую подпись. Сеть в итоге сходится к одному и тому же набору данных у всех участников, как git после нескольких pull’ов.

Зонд, который переживёт смерть своего хаба

Тут внимательный читатель ловит меня за руку. Вся статья про «нет единой точки отказа», а зонд-то отправляет результаты на один хаб. Этот хаб лёг — и что, зонд ослеп вместе с ним? Для системы, которая хвастается децентрализацией, это была бы досадная дыра ровно в том месте, где её обиднее всего иметь.

Поэтому зонд не привязан к одному хабу. В конфиге у него список «затравочных» хабов, но главное — он сам узнаёт о других хабах из сети. Каждый хаб публикует директорию известных ему хабов (и в которой URL’ы уже проверены на достижимость). Зонд её просто читает у того хаба, с которым сейчас работает, и складывает остальные хабы в карман как запасные. То есть достаточно указать зонду один живой хаб — а дальше он узнает про всю остальную сеть сам.

Дальше работает простое и предсказуемое поведение. Пока текущий хаб жив — зонд работает с ним. Хаб перестал отвечать — зонд автоматически перекатывается на следующий живой: регистрируется там, забирает у него назначение проверок, шлёт результаты ему. А когда домашний хаб оживает — зонд возвращается к нему на ближайшем хартбите (предпочтение в списке закреплено, так что зонд гравитирует к своему настроенному хабу). Упавший хаб ненадолго уходит в «остывание», чтобы зонд не долбился в труп на каждой попытке.

И что приятно — тут красиво сыграла исходная архитектура. Зонду всё равно, на какой хаб приземлится его результат: регистрация зонда не привязана к хабу (любой хаб примет валидно подписанного зонда и сам раздаст ему проверки), а результат всё равно разойдётся по всей сети через gossip. Так что когда домашний хаб вернётся, он получит всё, что зонд за время простоя слал чужим хабам — через ту же синхронизацию, что и любой опоздавший участник. Результат, который не удалось доставить текущему хабу, не выбрасывается, а тут же повторяется на следующем живом. Потеря возможна только если легла вообще вся сеть разом — но тогда у вас проблемы поинтереснее мониторинга.

«Проверка» против «подписки»: почему один сайт мониторится один раз

Проверка (check) контентно-адресуема. Её ID выводится из самого содержимого: тип, цель, параметры прогоняются через хэш. Это значит, что если сто человек добавят мониторинг https://example.com, то это будет одна проверка, которую сеть выполняет один раз — а не сто одинаковых проверок, дубасящих чужой сервер сотней запросов.

А то, что связывает лично вас с этой проверкой — это подписка (subscription): подписанная вашим ключом запись «слежу вот за этим». «Мои сервисы» на дашборде — это просто список ваших подписок.

Из этого красиво следует управление нагрузкой: проверка назначается на зонды, только пока у неё есть хотя бы одна живая подписка. Никому больше не интересен сервис — подписки истекают (у них есть срок жизни, дашборд их продлевает, пока вкладка открыта), проверка перестаёт назначаться, мощности высвобождаются. Capacity follows demand, без ручного управления.

Кто что проверяет и кто что хранит

Раз нет координатора, возникает два честных вопроса: как решается, какой зонд какую проверку выполняет, и как не заставлять каждый хаб хранить вообще всё.

Назначение проверок. Каждый хаб самостоятельно, жадным алгоритмом, раскладывает глобальный каталог проверок по своим зарегистрированным зондам. Он целится в нужную избыточность (несколько зондов на проверку, чтобы был кворум), не превышает заявленную зондом пропускную способность и назначает зонду только те типы проверок, которые тот умеет. Каждый хаб решает это локально и независимо — а глобальная избыточность возникает сама собой из того, что над общим каталогом работает много хабов. Никто никого не координирует, а в сумме всё покрыто.

Хранение. Поначалу каждый хаб хранил весь поток результатов всей сети. Это нормально, пока сеть маленькая, но не масштабируется. Поэтому управляющие данные (каталог, подписки, алерты, директория хабов — это всё мелочь) реплицируются полностью, а вот результаты шардируются. Каждая проверка попадает в шард, каждый шард хранят top-K хабов, выбранных взвешенным по ёмкости rendezvous-хэшированием — это такой способ детерминированно и без координатора договориться, кто за что отвечает (тот же приём, что и в распределённых кэшах). Каждый хаб вычисляет это сам из общей директории.

Важная деталь про безопасность по умолчанию: пока в сети мало хранящих хабов, все по-прежнему хранят всё. Расползание данных по шардам включается, только когда хабов становится достаточно. То есть соло-узел или маленький меш ведут себя ровно как раньше — никаких сюрпризов на старте.

Если на дашборде запросили проверку, данные по которой этот хаб не хранит, он сходит за ними к держателям шарда по HTTP — и, как вы уже догадались, перепроверит подписи, потому что ретранслятору веры нет и тут.

Алерты, которые работают без центра — и при этом приватны

Алерты в централизованной системе — это просто: есть сервер, он знает все правила, он шлёт уведомления. В децентрализованной так нельзя, и тут было больше всего интересной возни.

Правила алертов расходятся по всей сети, как и всё остальное. Но если правило знают все хабы — кто шлёт уведомление? Если каждый — вы получите N одинаковых писем «сайт лёг». Если механизм наивный — при сбое ответственного хаба уведомление не уйдёт вообще.

Решено так. Для каждого правила ответственность за отправку несёт ровно один хаб — тот, кого выбирает rendezvous-хэширование среди живых получателей. Он один оценивает условие и шлёт уведомление. Падение факта downtime подтверждается корроборацией: несколько независимых зондов должны видеть, что цель недоступна, и никто не должен видеть, что она доступна — это защита от ложных срабатываний из-за одной кривой точки обзора.

Если ответственный хаб замолчал — его «протухание» отслеживается по таймауту, и в течение нескольких минут ответственность подхватывает другой хаб-получатель. Доставка — at-least-once без потерь: статус «доставлено» продвигается только после успешной отправки, неудавшийся webhook не считается доставкой, и на следующем тике будет повтор. Хабы обмениваются подписанным состоянием доставки (last-writer-wins), так что хаб, подхвативший эстафету, знает, что уже отправлено, и не дублирует. Честно скажу: истинный exactly-once против внешних эндпоинтов недостижим в принципе (подтверждение об отправке может само потеряться), так что дубль теоретически возможен в коротком окне передачи ответственности. Я предпочитаю написать это прямо, а не обещать невозможное.

Теперь самое приятное — приватность. Куда именно слать алерт (ваш ntfy-топик, ваш webhook) — это чувствительные данные, а правила-то расходятся по всей сети, к операторам чужих хабов. Поэтому адрес назначения запечатан: браузер шифрует его (анонимный X25519-box) на ключи только тех top-K хабов, что отвечают за это правило. Оператор любого другого хаба видит в правиле лишь запечатанные байты — ни топика, ни URL. Отвечающий хаб распечатывает свой конверт и шлёт. Формат шифрования закреплён тестовым вектором JS→Go, чтобы браузер и сервер гарантированно понимали друг друга.

Каналы — это ntfy, Discord, Slack и «сырой» webhook. Все они устроены одинаково: обычный исходящий HTTPS-запрос на адрес, который указали вы. Поэтому оператору хаба не нужно вообще ничего настраивать — ни SMTP, ни ключей, ничего. (Раньше был ещё email через SMTP хаба, но он полагался на то, что операторы поднимут почтовые серверы, — выпилил в пользу универсальных HTTP-каналов.)

Почему почти всё подписано, и почему это важно

Если выкинуть один тезис из этой статьи в память, пусть будет такой: в открытой сети нельзя доверять никому, поэтому доверять надо подписям, а не узлам.

Подписано почти всё, что ездит по сети: результаты проверок, записи каталога, анонсы хабов, подписки, правила алертов, состояние доставки, даже регистрация зонда. Каждый узел проверяет подпись на входе сам и независимо. Это даёт устойчивость к враждебным участникам без всякого центрального удостоверяющего центра: пакость, которую вы вбросите в сеть, либо корректно подписана вами (и тогда вы за неё отвечаете), либо не пройдёт проверку и будет отброшена.

Отдельная тонкость — данные, которые подписывает браузер. JSON нельзя надёжно воспровести байт-в-байт между языками (порядок ключей, пробелы, экранирование), поэтому подписки и правила алертов подписываются не как JSON, а как явная каноническая строка. JS-кодировщик в браузере и Go-кодировщик на хабе обязаны выдавать одинаковые байты — это закреплено кросс-языковым тестовым вектором, который обе стороны должны воспроизвести из фиксированного зерна. Тронул кодировщик — изволь обновить обе стороны и перегенерировать вектор, иначе данные, созданные в браузере, перестанут верифицироваться. Звучит занудно, но это ровно тот клей, на котором держится доверие между браузером и сетью.

И сразу про честные ограничения, потому что обещать звёзды легко. Открытый меш уязвим к Sybil-атаке (наклепать тысячу фейковых зондов) — смягчение тут не в привратнике на входе, а в корроборации между независимыми зондами. А локация узла — самозаявленная: по умолчанию определяется по публичному IP, руками переопределяется. IP-геолокация — это удобство, а не доказательство. Proof-of-location честно вынесен в «отложено»: подтвердить криптографически, что зонд физически находится там, где говорит, — нерешённая задача, и я не делаю вид, что решил её.

Что в итоге получает пользователь

Если отвлечься от внутренностей, картинка для человека такая.

Поднимать сервер не обязательно. Заходите на дашборд, браузер генерирует вам ключ, добавляете монитор — и получаете проверки из всех точек, где люди подняли зонды, с географической картой: откуда сервис доступен, а откуда нет. Это та самая мультилокационность, за которую коммерческие сервисы берут деньги, — здесь она побочный эффект архитектуры.

Хотите дать сети точку обзора из своего города — поднимаете probe-узел одной командой, указав чужой хаб как домашний. Хотите быть полноправным участником федерации, хранить и разносить данные — поднимаете хаб. Один контейнер, и вы часть сети.

А возвращаясь к вопросу из заголовка прошлой статьи — кто мониторит монитор? В этой архитектуре вопрос как-то растворяется. Монитор — это не сервер, который надо сторожить, а сеть, которая сторожит сама себя. Узел упал — соседи этого не заметили. Это не отменяет остаточный риск (он есть всегда, об этом была вся та статья), но смещает его из разряда «единая точка отказа, которую мы обвешиваем подпорками» в разряд «свойство, размазанное по всем участникам».

Технический стек, коротко

Для тех, кому интересны гайки:

  • Ядро — Go, три модуля в одном go.work workspace: общий pkg (идентификация, протокол), probe, hub.

  • Mesh — libp2p: gossipsub для рассылки, DHT для обнаружения пиров. Версии жёстко запинены — в новых релизах есть неприятная неоднозначность со сплитом модуля core.

  • Хранилище — TimescaleDB. Результаты хранятся ярусами: свежие сырые (сжатые), потом почасовые и посуточные агрегаты, у каждого яруса своя политика хранения. Потому что каждый хаб тащит поток всей сети — детальность гасится с возрастом данных, чтобы диск не разрастался. Без базы хаб тоже работает (in-memory), удобно для локальных проб.

  • Криптография — Ed25519 для подписей, X25519 для запечатывания адресов алертов. В браузере — нативный WebCrypto, в Go — стандартная библиотека, между ними закреплённые тестовые векторы.

  • Дашборд — React + Vite + TypeScript, карта на MapLibre GL. Вся криптография аккаунта живёт в браузере.

  • SSRF-guard — отдельный общий пакет: проверки целей, доставка вебхуков и верификация директории ходят наружу только через SSRF-safe клиент, который перепроверяет резолвнутый IP в момент соединения. Мониторинг, которым можно простукивать внутреннюю сеть, — так себе мониторинг.

Послесловие

Прошлая статья была про смирение: идеального решения проблемы «кто мониторит монитор» нет, есть слои и остаточный риск. Эта — про попытку зайти с архитектурной стороны и убрать саму единую точку, которую надо было мониторить. Остаточный риск никуда не делся — он просто стал другим, размазанным и куда более дружелюбным.

Получилось ли «лучше, чем у всех»? Нет, и не было такой цели. Получилось «иначе», с понятными свойствами и честно описанными ограничениями. Мне кажется, в инфраструктурных штуках это и есть правильная планка.

Если зацепило — буду рад вопросам и критике в комментариях. Особенно по части того, где в этой модели доверия я не доглядел: ломать чужие схемы доверия Хабр умеет как никто.

Код, документация и трекер — на GitHub: github.com/mwgg/LibrePing. Лицензия AGPL-3.0; PR и issue приветствуются.