ADR 0162 — Broker становится пятым опубликованным образом, а установщик СПРАШИВАЕТ, включать ли его
Статус
Accepted. Решено сопровождающим 2026-09-18 (AT-097). Закрывает решение 7 ADR 0150 («broker в этой фазе не поднимается, и последствие названо»), расширяет ADR 0119 с четырёх до пяти образов и применяет к установке довод, который ADR 0146 (RN-512) применил к compose разработки. Сам broker — пять слоёв, пять операций, вычисляемая spec — это broker из ADR 0130, и в нём ничего не меняется. Ни один из них не редактируется; этот документ на них ссылается.
Брат: ADR 0161
поставил половину WEB того же решения сопровождающего («оба») — создание
проекта предлагает только режим, который установка исполняет, — и письменно
объявил, что половина УСТАНОВЩИКА останавливалась, потому что образ не
публиковался. Этот ADR — та половина. Они дополняют друг друга, не завися друг
от друга: тот не даёт экрану предлагать то, что установка не исполняет; этот
даёт установке способ исполнять, с согласием. При включённом broker у
установки появляется BROKER_URL, и именно по нему экран из 0161 снова
предлагает оба режима.
Контекст
В реальной установке проект container или mounted никогда не
исполняется. Контейнер этих двух режимов поднимает broker
(ADR 0144 ветвит по НАЗНАЧЕНИЮ:
container и mounted → broker), а у compose установки нет этого сервиса.
BROKER_URL остаётся неопределённой, container_start заканчивается
container.start_failed с BrokerIndisponivelError, а dev agents навсегда
остаются в dev.blocked_by_container — RN-502 по праву отказывается исполнять
вне контейнера running.
Решение 7 ADR 0150 это объявило и назвало исправление маленьким. Измерено 2026-09-18, при открытии этого направления:
docker manifest inspect ghcr.io/daneiel/brabo-broker:6.1.0→denied; тот же вопрос дляbrabo-api:6.1.0разрешается. Образа нет в registry.- В
docker-bake.hclбыло четыре цели (api,engine,web,backup), аscripts/ci/images-manifest.tsпринимал ровно эти четыре. docker/broker/Dockerfile.prodуже существовал и уже проходил черезhadolintвci.ymlвместе с остальными четырьмя —FROMпо digest с тегом на строке выше,USER node,HEALTHCHECKпротив/health, npm/corepack удалены. Недоставало не образа: недоставало того, кто его соберёт, просканирует и опубликует.ci.ymlникогда его не собирал;docker-compose.prod.ymlссылался на него какbrabo-broker:prod, который существовал лишь на машине того, кто запустилdocker compose buildс profile.- Release не несёт код, поэтому
--source=local— путь, который ADR 0150 указывал как выход, — требует checkout на теге, а это не установка одной строкой.
Решение
1. Broker — ПЯТЫЙ опубликованный образ, с теми же gates, что у остальных
Цель broker в docker-bake.hcl, внутри группы default: ci.yml начинает
собирать его на каждом PR, проверять, что он не работает от root, сканировать
его в Trivy по тем же правилам (HIGH,CRITICAL, --ignore-unfixed) и
доказывать, что он ПОДНИМАЕТСЯ healthy с rootfs только для чтения.
release.yml публикует его по DIGEST, images-manifest.ts его требует
(недостающая цель по-прежнему проваливает — пять из пяти или ничего), а
keyless-подпись cosign и проверка в том же запуске
(RN-524) покрывают его без новой строки, потому
что оба цикла читают манифест.
Kubernetes о нём не знает, и это решение, а не забывчивость: в
deploy/k8s/ нет Deployment broker, а монтирование сокета узла в pod — другой
разговор о привилегиях. argumentosDeSetImage начинает выдавать только
образы, которые объявляет база kustomize, — make imagens-do-release
по-прежнему пишет четыре строки в overlay.
2. Compose установки получает сервис, ВЫКЛЮЧЕННЫЙ по умолчанию
Сервис входит под profiles: ["container-broker"], ТЕМ ЖЕ profile, что в
compose проверки, и с пятью слоями ADR 0130 нетронутыми: без ports:, только в
сети broker (internal: true, которой достигает только api и у которой нет
egress), BRABO_SERVICE_TOKEN обязателен в продакшене, пять операций, spec,
вычисляемая из того, что говорит api. Сокет монтируется в нём и ни в каком
другом. api начинает всегда участвовать в сети broker — как уже делает в
compose проверки, — а BROKER_URL в api остаётся пустой, если только .env не
говорит иного.
Образ берётся из BRABO_BROKER_IMAGE, обязательной, как и остальные
четыре, и это измерено, а не выбрано: Compose интерполирует ВЕСЬ файл до
фильтрации по profile, поэтому ${VAR:?…} в выключенном сервисе отклоняет
файл точно так же. Установщик всегда её записывает (она приходит из того же
манифеста), включает он broker или нет.
Два корня — как в compose проверки: BRABO_PROJECTS_HOST_BASE выводится из
BRABO_PROJECTS_BASE, которая уже путь хоста. Другой,
PROJECT_WORKSPACES_HOST_ROOT, невозможно вывести в compose — управляемая
папка установки — это ИМЕНОВАННЫЙ том project_workspaces, а путь, который
разрешает демон, — путь ХОСТА. Установщик его ВЫЧИСЛЯЕТ
(<DockerRootDir>/volumes/brabo_project_workspaces/_data, раскладка драйвера
local) и после подъёма СВЕРЯЕТ с Mountpoint, который демон возвращает для
тома; расхождение становится названным хвостом с правильным значением,
никогда молча угаданным значением.
3. Установщик СПРАШИВАЕТ, с риском, сказанным текстом, и по умолчанию — НЕТ
Новый вопрос, после базы и до .env. Он говорит, что предоставляет включение
— сервис этой установки начинает говорить с Docker этой машины, и кто
командует этим сервисом, командует Docker, — что сдерживают слои и что
меняется без него (проекты container и mounted не исполняются; режим Runner
от него не зависит). Включает только s/sim. Enter, что-либо другое или
отсутствие терминала оставляют выключенным, и скрипт ГОВОРИТ, что осталось
выключенным.
«Да» записывает в .env COMPOSE_PROFILES=container-broker,
BROKER_URL=http://broker:8090, DOCKER_GID и вычисленный корень.
DOCKER_GID измеряется изнутри контейнера, самим образом broker и с
сокетом, смонтированным через --mount type=bind (который отказывает, а не
создаёт папку, когда источника нет), потому что group_add нужен именно gid,
который процесс видит там, — а в Docker Desktop gid сокета на хосте ничего не
говорит о gid внутри VM. Невозможность измерить или то, что путь не является
сокетом, — НАЗВАННЫЙ отказ, обучающий запустить снова и ответить «нет»;
никогда угаданный 999.
Решение появляется в итоговой сводке, а фраза «чего этот установщик НЕ
делает» перестаёт говорить, что без контейнера остаётся только «Pasta
montada»: container тоже остаётся.
4. Почему довод RN-512 действует здесь
ADR 0146 разделил два compose по тому, что ЗНАЧИТ сокет: в разработке тот, кто поднимает стек, уже имеет сокет, так что получение его broker не предоставляет ничего, чем оператор не владел бы; в продакшене сокет — граница привилегий, и оператор — не разработчик.
Установка одной строкой — первый случай, а не второй. Она работает на
машине самого человека, который только что выполнил docker compose up, —
установщик не работает без того, чтобы у него был сокет. Оператор и
пользователь — один человек. Чего установка НЕ может предполагать, и поэтому
она спрашивает, а не включает, как compose разработки, — что тот, кто
устанавливает, знает, что такое сокет: compose dev читает тот, кто
разрабатывает продукт; установщик запускает тот, кто никогда не открывал этот
репозиторий. Явное согласие — оставшееся различие, и именно его покупает этот
ADR.
docker-compose.prod.yml не меняется: он остаётся под profile, и для него
прочтение ADR 0146 действует без оговорок.
Последствия
Цена, объявленная.
- Root-эквивалентный образ становится публичным. Любой может скачать
ghcr.io/daneiel/brabo-broker. Сам образ ничего не предоставляет — предоставляет смонтированный сокет, — но он становится изучаемой целью, и уязвимость в нём превращается, в каждой установке, где его включили, в путь до Docker хоста. Поэтому он входит в Trivy каждого PR и в подпись каждого тега, а не в отдельный список. - Сокет Docker начинает монтироваться в сервис пользовательской
установки, когда она соглашается. Кто командует broker, командует Docker
машины. Что его отделяет: он не публикует порт, его достигает только api,
токен сравнивается за постоянное время, и нет поля, куда можно написать
privileged,cap_add,network: hostили свободный-v. Это сдерживает вызывающего; это не сдерживает баг в самом broker. BRABO_BROKER_IMAGEстановится обязательной в.envустановки, включено или нет..env, записанный предыдущим установщиком, её не имеет — но установщик перезаписывает.envпри каждом запуске, а обновление идёт через него.- Установщик требует
brokerвimages.json. У Release, предшествующей этой, его нет, и установщик этой версии отказывает, называя цель. Это то же правило, что в ADR 0160: установщик тега устанавливает этот тег. - Управляемый корень зависит от раскладки драйвера
local. Вычисляется до подъёма, сверяется после; несовпадение становится хвостом со значением, прочитанным у демона. - Не измерено:
DOCKER_GIDи вычисленный корень в Docker Desktop на macOS. E2E тега выполняет интерактивную половину только в Linux.
Что НЕ меняется. Compose проверки, smoke.sh (который поднимается без
profile и продолжает доказывать те же три образа приложения), compose
разработки, Kubernetes и сам broker.