ADR 0161 — The screen only offers the mode the installation executes, and the Release installation has no broker
Status
Accepted. Decided by the maintainer on 2026-09-18 (AT-085), with the answer "both" to the question "does the installer turn on the broker, or does the screen stop offering what does not run?". It revises the preselection of RN-513 (ADR 0146, point 4) and adds a local refusal reason to RN-521. Neither is edited; this document references them. Only the SCREEN half was delivered — the installer half was measured and stopped, for the reason in the section "The half that stopped".
Context
A real installation of v6.1.0, done through the one-liner on 2026-09-14, created
a project in mounted through the web. It was the preselected mode:
install.sh always writes BRABO_PROJECTS_BASE (the consented base,
RN-531), and with a known base
RN-513 preselects mounted. What followed in the
event log:
- 6×
dev.started→ 6×dev.blocked_by_container; - 2×
container.start_failedwith "BROKER_URL não está definida — o broker de container não faz parte desta instalação…".
Nobody works on that project, ever. container and mounted only bring up a
container through the BROKER (ADR 0144), and
without a running container no dev agent command runs
(ADR 0143). Only runner does
not depend on it: whoever brings up the container is the local agent, on the
user's machine (ADR 0137). And
the /containers page let two container_start that could only fail be
approved.
Measured, and different from the activity's description: the installation
compose did not "inherit the broker under a disabled profile". The broker
service does not exist in docker/docker-compose.install.yml — its header
says so at point 3 —, and the absence was decided in writing in decision 7 of
ADR 0150, which also declared it "in the
installer's output". The defect is not the installer hiding the absence: it is
the SCREEN not knowing about it, and offering — even preselecting — the mode the
absence makes useless.
The half that stopped: the installer cannot obtain the broker image
The maintainer's decision had a part (a): install.sh asking whether to turn on
the broker, with the risk stated in text (the Docker socket mounted in a service
is root-equivalent on the host). Before writing the question, it was measured
whether the installation can have the image the "yes" answer would bring up.
It cannot:
docker-bake.hclhas four targets (api,engine,web,backup);scripts/ci/images-manifest.tsdoes not knowbroker; and theimages.jsonof the v6.1.0 Release lists exactly those four;docker manifest inspect ghcr.io/daneiel/brabo-broker:6.1.0answersdenied(the repository does not exist), while the SAME anonymous command forghcr.io/daneiel/brabo-api:6.1.0resolves — it is not a lack of credentials;- the v6.1.0 Release assets do not bring
docker/broker/Dockerfile.prodnor the code ofapps/broker/packages/docker-port, so a one-liner installation, which runs in a folder without a checkout (ADR 0160), does not even have anything to build.
A question whose "yes" has no image to bring up is worse than none: the person
consents to the socket risk and receives a pull access denied. Only
--source=local (clean checkout, on a tag) could build the broker — and even
there the installation compose does not have the service. The fix is the one ADR
0150 already named as small and outside its scope: publish the broker as a fifth
image (a target in the bake, an id in the manifest, one more signature), and ONLY
THEN the installer's question. It is left for its own front, and it is that front
that reopens this half. install.sh was not touched by this ADR.
Decision
1. The api says whether the installation has a broker, in the two routes the screen already reads
brokerConfigurado: boolean enters two existing responses and no new route:
GET workspaces/:workspaceId/projects-base— the creation wizard's question was already "which mode can this INSTALLATION execute?", and the base was only half of it. Same minimum (maintainer), same caller, same instant;GET workspaces/:workspaceId/containers— per ROW, with the same value in all. The response is a list, and swapping it for an envelope would break the contract for a field that belongs to the installation.
The source is ContainerBrokerPort.configurado(), the SAME one that decides
whether the observed-state read asks the broker or declares absence — reading
BROKER_URL a second time would be the second source that one day diverges. The
field says the variable EXISTS, never that the broker ANSWERS: whoever knows that
remains naoObservado (RN-486).
2. The wizard does not preselect mounted without a broker, and offers runner
Three states, which do not collapse:
- confirmed broker (
true): RN-513 behavior intact — with a base,mountedpreselected; - confirmed absence (
false):containerandmountedstay ON THE SCREEN (removing the card would hide the information — the rule "remove the control, never the information" of RN-102) but inert, the reason is stated ONCE in text below them, andrunneris the preselected one. A human choice made before the answer arrives, in a mode it makes unexecutable, falls back torunner— the same rule that already droppedmountedwhen the base disappears; - don't know (loading, failing query, absent field): neither "has" nor "does
not have". It does not preselect
mounted— the revision of RN-513 —, and it also does not lock cards nor assert in text an absence nobody confirmed:containerstays selected, as before.
3. /containers refuses the startup before the click, with its own reason
decidirSubida gains the reason sem_broker_na_instalacao, for container and
mounted when brokerConfigurado !== true — "don't know" does not become
"has". It comes right after ja_esta_de_pe and BEFORE sem_imagem_decidida: in
an installation with nobody to bring up the container, sending someone to decide
the image would be pointing to the wrong door. runner never falls into it. The
branching is the same by DESTINATION as acaoDeSubidaDoModo (whoever depends on
the broker is whoever proposes container_start), read on the other side by
usaBroker, and not a second rule.
Consequences
- In a Release installation, the mode that works end to end becomes the
suggested one, and the one that does not work starts saying why before anyone
chooses it. The price:
runnerrequires the local agent up and Docker on the user's machine — which the one-liner installation already delivers (ADR 0155). - In the DEVELOPMENT compose the broker comes up by default
(RN-512), but
BROKER_URLstays empty until someone puts it in the.env(docker/docker-compose.yml,.env.example). Without it, the developer's wizard now shows the warning — and it is RIGHT: withoutBROKER_URL, the api never calls the broker that is up, andcontainer_startwould fail the same way. - RN-512 does NOT change: the broker remains under
profiles: ["container-broker"]in the production compose, outside the installation compose, and none of the five containment layers is touched. What changes is the SCREEN stopping assuming the opposite.
Out of scope, declared:
- the installer half (section above) — it depends on publishing the image;
- mode conversion (
ExecutionModeSection, ADR 0111) can still convert tocontainer/mountedin an installation without a broker; it reads the same route and the field is at hand, but it is another screen, with another offering rule (RN-559); - stopping and removing (
container_stop/container_remove) did not gain the refusal: without a broker there is nocontainer/mountedcontainer registered as up to stop, and the case was not measured; - the Infra Lead does not know about an absent broker — it refuses by MODE (RN-566), and enriching its context with the installation is the more expensive front RN-566 already declares;
- the parallelization suggestion that ignores an agent blocked by a container;
- the closing sentence of
install.shsays that without a broker "a project in Mounted folder mode does not bring up a container" and omits thecontainermode, which does not either — an adjacency measured and not fixed here, because the installer belongs to another parallel front.