Files
hq/04-ISSUES/094-a-port-published-as-the-machine-side-cannot-be-moved/00-report.md
T
jschoubben 668ce3ad62 Issue 094 resolved: a given port names either end and is answered once
The first pass answered under both ends, which review showed is the same fault seen
from the other side where two mappings share a number. Verified on the machine: the
forge is back on the port its own configuration has always advertised.
2026-09-23 02:37:07 +02:00

60 lines
3.0 KiB
Markdown

---
status: resolved
opened: 2026-09-23
located-in: [mesh-controller internal/catalogue/filtering.go, mesh-controller cmd/mesh-controller/plan.go]
fixed-by: mesh-controller — a node may move a port a module publishes as a mapping's machine side
amended-design:
---
# 094 — A port published as the machine side of a mapping cannot be moved, and trying blocks every push
## What was observed
On the control-node, immediately after the first module was migrated, 2026-09-23.
A module publishes two ports. One is written short, so the machine's side and the software's side
are the same number. The other is written long: **a machine port mapped to a different port inside
the container**, because the software's own port is one the machine's own daemon already holds.
The module's `listens` names the **machine** side of that mapping, deliberately, and says why.
The predecessor served that port on a different machine port. Moving the module's to match — the
whole point of a port being a per-node setting
([ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md)) — was
refused:
```
gives port 2222 a machine port, and no container of its publishes 2222 —
the mesh cannot move a port the module does not publish
```
It does publish it. The check collects only the **last** segment of each published entry, which is
the container's inner port, so for a long-form mapping it sees the wrong half. Naming the inner
port instead is accepted and then never consulted, because the lookup is by what the module says
it listens on — which is the machine side.
**And the refusal is not confined to the setting.** While that setting exists, composition fails,
so **every push to that node is refused** until it is removed. A setting that cannot work should
be refused where it is set, not where it is read.
The same blind spot appears one step further on: on an adopted node **no opening was derived** for
that port either, and a per-node `expose` for it produced none, so the firewall never opened what
the module serves.
## Why it matters beyond this instance
Every module that maps a machine port to a different port inside its container has this hole, and
the pattern is common precisely where it matters: a service whose own port collides with something
the machine already runs. It is also exactly the case a migration hits, because matching the
predecessor's port is how a service keeps working when it changes hands.
The push-blocking half is worse than the feature gap. One unusable setting stops a machine being
told anything at all, and the message names a port rather than the setting that caused it.
## Open questions
- Should a setting name the port the software listens on, the machine port, or either, and what
refuses the ambiguous case where a module publishes both halves of some other mapping?
- Should `settings set` validate against the module's manifest at the moment it is set, so an
impossible setting cannot be stored?
- Should composition fail the whole node, or fail that module and send the rest?