diff --git a/04-ISSUES/028-two-things-want-one-port-and-nothing-says-so/00-report.md b/04-ISSUES/028-two-things-want-one-port-and-nothing-says-so/00-report.md new file mode 100644 index 0000000..a4676c9 --- /dev/null +++ b/04-ISSUES/028-two-things-want-one-port-and-nothing-says-so/00-report.md @@ -0,0 +1,57 @@ +--- +status: located +opened: 2026-09-01 +located-in: [mesh-control] +fixed-by: +amended-design: +--- + +# 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 `serves` means 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.