Found by fixing 027 and pushing again. The declaration is now accepted and the database container still cannot start: the mesh's own store holds 5432 on that machine, and the module publishes 5432. Nothing catches it because the substrate is not a module. It arrives from the bundle before there is a mesh to ask, so the control plane has never heard of the store and does not know it holds a port. Resolution can compare modules with each other and cannot compare one against what the mesh is built on. Nor does it compare modules with each other. A port is exclusive on a machine in exactly the way a claim is, and the mesh has a mechanism for that which ports do not use. It has been met before: the end-to-end test that exercises a real database publishes 5433 rather than 5432, inline, with nothing saying why. That is how a constraint becomes folklore. The open question is bigger than the bug. Whether a module should publish to the machine at all decides how a consumer reaches it, and changes what `serves` means.
2.6 KiB
status, opened, located-in, fixed-by, amended-design
| status | opened | located-in | fixed-by | amended-design | |
|---|---|---|---|---|---|
| located | 2026-09-01 |
|
028 — Two things want one port, and nothing says so until the machine
Symptom
The database module cannot start on a machine that runs the control plane:
Bind for 127.0.0.1:5432 failed: port is already allocated
The mesh keeps its own store on that machine, from the bundle, and it holds 5432. The module publishes 5432 too. Everything up to the machine is content: it resolves, it composes, it is pushed, and it is applied — the container is simply the one resource that fails.
Why nothing catches it
The substrate is not a module. It arrives from the bundle a host carries, before there is a
mesh to ask. So the control plane has never heard of mesh-store and does not know it holds a
port. Resolution can compare modules against each other and cannot compare a module against the
thing the mesh is built on.
And nothing compares modules against each other either. A port is exclusive on a machine in exactly the way a claim is — one seat, one display server, one artifact store — and the mesh has a mechanism for that, which ports do not use. Two modules both publishing 5432 would meet the same wall, one machine later.
It has been met before, and worked around
The end-to-end test that exercises a real database publishes 5433:5432 rather than 5432:5432.
The workaround is right there, inline, with no note saying why — which is how a constraint becomes
folklore.
What a fix has to settle
- Whether a module should publish to the machine at all. Consumers reach a provider by the
machine's address and the port it serves, so publishing is what makes that true. An alternative
is that they reach it on the module's own network by name, and nothing is published — which
changes what
servesmeans and is a larger decision than it looks. - Where the substrate's ports are written down. Whatever compares them needs to know what the bundle holds. The bundle is a list of pinned references; what those containers bind is not in it.
- What a refusal should say. 5432 is held by the mesh's own store on this machine is a useful sentence. Port is already allocated, arriving from a container runtime three layers down, is not.
Not the same as a firewall rule
listens already says which ports a module accepts on, and filtering is computed from it. That is
about what may reach a port from elsewhere. This is about two things on one machine wanting to own
the same one, which listens does not model and could not answer.