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.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
---
|
||||
topic: what runs on it
|
||||
status: proposed
|
||||
date: 2026-09-01
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
rests-on: 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`](../04-ISSUES/028-two-things-want-one-port-and-nothing-says-so/00-report.md)).
|
||||
|
||||
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.
|
||||
@@ -3,7 +3,7 @@ status: located
|
||||
opened: 2026-09-01
|
||||
located-in: [mesh-control]
|
||||
fixed-by:
|
||||
amended-design:
|
||||
amended-design: 02-DECISIONS/0038-the-mesh-assigns-the-port.md
|
||||
---
|
||||
|
||||
# 028 — Two things want one port, and nothing says so until the machine
|
||||
@@ -55,3 +55,19 @@ folklore.
|
||||
`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.
|
||||
|
||||
## Answered in principle
|
||||
|
||||
[ADR 0038](../../02-DECISIONS/0038-the-mesh-assigns-the-port.md), proposed the same day: **the mesh
|
||||
assigns the machine-side port and a module does not care.** A module cannot choose well, because it
|
||||
is written once and assigned anywhere — any number it picks is a guess about a machine it has never
|
||||
seen.
|
||||
|
||||
A port fixed by its protocol — mail on 25, submission on 587 — becomes a **claim**, which is the
|
||||
mechanism 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 earns the same refusal at assignment rather than at apply.
|
||||
|
||||
The record also names what this issue missed: the same number is written **three times** in every
|
||||
module — once for the rule set, once for what a consumer is told, once for what the runtime
|
||||
publishes — and nothing checks that they agree. A module whose `serves` and whose container
|
||||
disagreed would hand every consumer a port that answers nothing.
|
||||
|
||||
Reference in New Issue
Block a user