Куда развивать Netgap: размышления о направлениях и порядке работ
Важно. Это размышления, а не план работ. Ни один пункт ниже не является обязательством, ни один порядок — зафиксированным роадмепом, ни одна версия в примере — обещанием даты. Я разбираю, какие направления развития вообще есть у Netgap, чем они отличаются по цене и отдаче и почему один порядок работ мне кажется разумнее другого. Через месяц измерения или запрос от пользователя могут переставить приоритеты — и это нормально.
Три предыдущих поста дали материал, из которого можно строить рассуждение о развитии: измерения MVP показали, где стоит потолок производительности, каталог оптимизаций — какие приёмы имеют смысл, а каталог вариантов logical airgap — какое вообще есть пространство решений. Здесь я пытаюсь свести это в одну картину: что делать дальше и в каком порядке.
Отправная точка: где Netgap сегодня
На момент написания этой статьи Netgap — это комбинация трёх позиций каталога вариантов:
- 2.2 pull-модель — основа. Исполнитель
netgap-executorиз доверенного сегмента сам подключается к двум gRPC-мостам шлюза; доверенный сегмент не принимает ни одного входящего соединения. - 2.1 терминация TCP —
netgap-gatewayв недоверенной зоне поднимает HTTP-сервер, полностью завершает клиентское соединение и держит его в пуле, отслеживая таймауты и брошенные соединения; исходящий вызов целевого сервиса делает уже исполнитель. - 4.1 RPC с узкой схемой — контракт мостов описан двумя короткими
.proto:RequestBridgeApi.GetNextRequestотдаётcorrelation_id,headers,body,path,methodиtrace_context, аResponseBridgeApi.SendResponseпринимаетcorrelation_id,headers,body,status_code,trace_contextиtarget_processing_time. Больше через границу не проходит ничего.
Между запросом и ответом состояние живёт в асинхронной очереди в памяти шлюза — в терминах каталога это посредник уровня 3, но локальный, внутри процесса в недоверенной зоне, а не отдельная инфраструктура. Оба RPC сейчас унарные: исполнитель забирает задачи опросом с адаптивным интервалом. Сквозная корреляция, логи аудита по каждому запросу, разделение задержки на накладные расходы Netgap и время работы целевого сервиса, трассировка через OTLP, метрики Prometheus и события в SIEM — все это уже реализовано.
В текущей реализации Netgap нет ни взаимной аутентификации мостов (mTLS), ни лимита длины очереди, ни ограничений на размер тела и набор допустимых путей. Это не скрытые недостатки, а честная граница первой версии — и одновременно готовый список ближайших работ.
По каким критериям я сравниваю направления
Чтобы порядок работ не сводился к «сделаем то, что интереснее писать», каждое направление ниже оценивается по четырём осям:
| Ось | Вопрос | Зачем нужна |
|---|---|---|
| Трудозатраты | Это работа в существующих компонентах или новая сущность? | Отсекает красивое, но неподъёмное на текущем этапе |
| Прирост функциональности | Что пользователь сможет сделать после, чего не мог до? | Отличает функцию от рефакторинга |
| Расширение применимости | Сколько новых сценариев и строк каталога открывается? | Один шаг может открыть три сценария |
| Ценность для продвижения | Снимает ли шаг возражение, которое обесценивает весь проект? | Без mTLS и фильтрации продукт не проходит первый чек-лист |
К этим четырём осям я добавляю правило, выведенное из сопоставления каталога оптимизаций с измерениями MVP: работать имеет смысл только в том слое, где находится текущий потолок. В MVP потолок стоял в однопоточности брокера — и любые микрооптимизации Rust-кода не сдвинули бы сквозной RPS вообще. Сейчас в боевой архитектуре брокера нет, очередь живёт в памяти шлюза, а значит потолок нужно заново измерить, прежде чем что-то в нём оптимизировать. Пока измерений на боевом контуре нет, любые пункты «про производительность» в рассуждениях ниже стоят ниже пунктов «про применимость».
Что и в каком порядке реализовать
Базовый сценарий один и тот же во всём разделе: внешний клиент синхронно вызывает API во внутреннем контуре. Порядок ниже определяется не номерами версий, а тем, насколько шаг расширяет этот сценарий на единицу вложенных усилий.
Цвет ячейки в таблицах ниже — это оценка значения внутри своей колонки, от самого выгодного к самому невыгодному:
Шкала у каждой колонки своя: в «трудозатратах» зелёное — это меньше, а в «приросте функциональности» и «ценности для продвижения» зелёное — это больше.
Шаг 1. Дёшево и сразу заметно
Все четыре пункта — работа в существующих компонентах, без новых сущностей. Они же закрывают ровно те замечания, которые возникают при первом чтении конфигурации.
Шаг 2. Превращает продукт в платформу вариантов
Это самый выгодный шаг по соотношению «усилия к результату»: он не добавляет ни одной новой функции для конечного пользователя, но открывает сразу несколько строк каталога.
Шаг 3. Открывает соседние сценарии малой кровью
Шаг 4. Дорогое и нишевое
Логика порядка простая и та же, что в каталоге оптимизаций: сначала снимается то, что мешает применить продукт вообще (шифрование и фильтрация), потом расширяется пространство применимости (транспорты), потом добавляются соседние сценарии, и только затем — дорогая ниша.
Как выглядел бы роадмеп
Ещё раз: это пример раскладки шагов по релизам, а не план выпусков. Номера версий условны, дат нет намеренно — они появятся только когда шаг будет реально начат.
| Условная версия | Что входит | Критерий готовности |
|---|---|---|
| 0.x + 1 | mTLS на обоих мостах, allow-list методов и путей, лимит размера тела, ограниченная очередь с 503 | Запуск без mTLS невозможен по конфигурации; запрос вне allow-list не доходит до исполнителя и попадает в аудит |
| 0.x + 2 | Server-streaming на RequestBridge, замеры сквозной задержки до и после | Измеренная медианная задержка ниже прежней минимум на половину интервала опроса; исполнитель без задач не создаёт трафика |
| 0.x + 3 | Абстракция транспорта моста, gRPC как первая реализация поверх неё | Смена транспорта — правка конфигурации, а не кода; набор тестов моста проходит для каждой реализации |
| 0.x + 4 | Подписанный конверт сообщения, ротация ключей | Компрометация посредника не позволяет подменить сообщение; проверка подписи обязательна и выключается только явно |
| 0.x + 5 | Односторонний режим приёма, спул-транспорт | Работает контур, где между сегментами нет сетевой связности вообще |
| 1.0 | Стабильные схемы мостов, зафиксированный формат конверта и конфигурации, полный набор метрик и аудита | Обратная совместимость контрактов заявлена и проверяется тестами |
| после 1.0 | Ticket/callback, маршрутизация на несколько целей, инспекция содержимого, интеграция с диодом | По запросу, а не по инерции |
Отдельно про производительность. Пока боевой контур не измерен так же честно, как MVP, я не готов писать в роадмеп ни одной оптимизации. Порядок здесь тоже понятен из предыдущей статьи: сначала снять ограничения ОС и убрать блокировки, потом аллокации и копии, потом расшить посредника — и только затем affinity, io_uring и прочее. Первым же шагом в этом направлении будет не оптимизация, а воспроизводимый нагрузочный стенд на боевых компонентах: без него любая оптимизация — угадывание.
Чего делать не стоит
Отказ от варианта — такое же решение, как и его реализация, поэтому явно:
- Reverse-tunnel (2.3) как режим работы продукта. Даёт удобство ценой сквозного потока внутри туннеля — то есть ровно того, что паттерн должен устранять. Это будет не Netgap, а ещё один connector.
- SPA и port-knocking (2.5) внутри продукта. Полезный периметровый приём, но он не создаёт разрыва и относится к зоне ответственности межсетевого экрана, а не шлюза.
- Собственный брокер как обязательный посредник (3.1). MVP уже показал, что брокер становится потолком производительности; очередь в памяти шлюза дешевле и быстрее. Брокер имеет смысл только как одна из реализаций транспорта после шага 2 — для тех, у кого он и так есть.
- Разделяемая память между ВМ (3.6). Микросекундные задержки красивы, но требуют, чтобы оба сегмента жили на одном гипервизоре. Это противоречит основному сценарию, где сегменты разнесены.
- Экзотика уровня 5. Оптические, акустические и бумажные каналы в продукте не нужны: их пропускная способность несовместима с синхронным API, а как транспорт для одностороннего режима они проигрывают спулу и диоду по всем параметрам, кроме зрелищности.
- Kernel bypass и zero-copy на границе. Разобрано в каталоге оптимизаций:
spliceотменяет инспекцию содержимого, а инспекция и есть смысл шлюза. К тому же потолок сегодня не там. - Неограниченные очереди ради пиковых цифр. Оттуда же: без backpressure всплеск нагрузки превращается не в замедление, а в отказ по памяти.
Что я считаю главным принципом
Строгость изоляции нужно выбирать не максимальную, а достаточную для сценария — и дальше вкладываться в то, чтобы выбранный уровень был реализован без дыр. Аппаратный диод, поставленный ради галочки перед сервисом, который всё равно должен отвечать синхронно, защищает хуже, чем аккуратно сделанная pull-модель с mTLS и валидацией схемы.
Из этого принципа и вытекает вся раскладка выше: шаг 1 закрывает дыры в уже выбранном уровне, шаг 2 даёт возможность выбирать уровень под сценарий, а шаги 3 и 4 добавляют уровни, которых сейчас нет.
Важно. Всё вышеизложенное — рассуждение, а не фиксация планов. Порядок шагов отражает моё сегодняшнее понимание соотношения «усилия — результат» и может измениться от первого же серьёзного запроса или первого же честного замера. Актуальное состояние работ всегда стоит смотреть в репозитории и в документации, а не в блоге.
Это четвёртый технический пост серии. Ранее: измерения MVP и локализация узкого места, каталог оптимизаций logical airgap и анализ вариантов реализации паттерна.

