Files
hq/02-DECISIONS/0038-the-mesh-assigns-the-port.md
T
jschoubben c2a37ab7f8 38 — the mesh assigns the port, and a module does not care
Jochen's call, and the right one: a module cannot choose a port well,
because it is written once and assigned anywhere. Any number it picks is
a guess about a machine it has never seen, and two modules guessing the
same number is not a mistake either of them made.

Writing it up turned up something the issue had missed. The same number
appears three times in every module — the rule set, what a consumer is
told, and what the runtime publishes — and nothing checks that they
agree. They agree today because one person wrote all three. A module
whose `serves` said one thing and whose container published another
would resolve, compose, apply, and hand every consumer a port that
answers nothing.

So the decision is one source with the other two derived, and an
assignment made once and kept, as a credential is.

The part that needed thought is ports that cannot move — mail on 25,
submission on 587. Those become claims, which is what the mesh already
has for what is singular on a machine. Two modules wanting 25 is the
same shape as two wanting the seat, and gets refused by name at
assignment rather than by a container runtime at apply. That makes this
mostly a matter of pointing an existing mechanism at ports.

Left open: whether a module should publish to the machine at all.
Assignment makes publishing safe without making it necessary.
2026-09-01 17:42:27 +02:00

3.9 KiB

topic, status, date, deciders, reconstructed, rests-on
topic status date deciders reconstructed rests-on
what runs on it proposed 2026-09-01 jochen false 02-DECISIONS/0009-modules-and-the-graph.md

38. The mesh assigns the port, and a module does not care

The problem, as met

A database module cannot start on a machine that runs the control plane. The mesh keeps its own store there and holds 5432; the module publishes 5432. Nothing notices until a container runtime three layers down says port is already allocated (028).

A module cannot fix this by choosing better, because a module cannot know what else is on the machine. It is written once and assigned anywhere. Any number it picks is a guess about a machine it has never seen, and two modules guessing the same number is not a mistake either of them made.

The number is written three times, and nothing makes them agree

Every module says its port in three places:

where for example
listens the rule set that lets traffic in {port: 5432, from: mesh}
serves what a consumer must know to connect {port: 5432}
a container's ports what the runtime publishes "5432:5432"

They agree today because one person wrote all three. Nothing checks it. A module whose serves said 5432 and whose container published 5433 would resolve, compose, apply, and hand every consumer a port that answers nothing.

The decision

The mesh assigns the machine-side port, and the module says only what it needs. A module declares that a container port must be reachable and what it is for. Which number the machine uses is the mesh's to choose, because the mesh is the only thing that knows what else is there.

One source, and the other two are derived. serves carries the assigned port so a consumer is told where to connect without the module having written it down; the rule set is computed from the same assignment. Three copies become one fact.

An assignment is made once and kept, exactly as a credential is. A port that moved on every push would restart both ends each time and would hand consumers a number that was true when it was read.

Some ports cannot move, and that is a claim

Mail is 25, submission is 587, IMAP over TLS is 993. A mail system on a strange port is not a mail system. So a module may say a port is fixed by the protocol rather than assigned.

A fixed port is exactly a claim — the thing the mesh already has for what is singular on a machine: one seat, one display server, one artifact store. Two modules wanting 25 on one machine is the same shape as two wanting the seat, and gets the same answer: the second is refused, by name, when it is assigned rather than when it is applied.

That is why this does not need a new mechanism so much as it needs the existing one pointed at ports.

What follows

  • A module becomes portable in a way it was not. Two databases on one machine stop being a collision and become two assignments.
  • The substrate has to be visible. The mesh cannot assign around its own store while it has never heard of it. What the bundle holds must be written down somewhere the assignment can read — which the bundle does not say today.
  • A refusal can be useful. 25 is held by the mail system on this machine is a sentence a person can act on. port is already allocated is not.
  • serves stops being written by hand, which is a small vocabulary change with a large consequence: what a consumer is told is now derived from what actually happened.

What this does not settle

Whether a module should publish to the machine at all. Assignment makes publishing safe; it does not make it necessary. Consumers could instead reach a provider on the module's own network by name, with nothing published — which would make the question moot for anything inside the mesh, and would still leave it for anything reached from outside.