Issue 093: the successor proxy cannot serve what the predecessor still serves #80

Merged
jschoubben merged 1 commits from issue/093-proxy-handover into main 2026-09-22 21:57:05 +00:00
@@ -0,0 +1,64 @@
---
status: open
opened: 2026-09-22
located-in: []
fixed-by:
amended-design:
---
# 093 — The successor proxy cannot serve what the predecessor still serves
## What was observed
On the control-node, hours after it was adopted, 2026-09-22.
Assigning the first web module to the adopted node was refused:
```
nothing provides "route", wanted by <module>
```
Every module that must be reachable by name requires a **route**, and the mesh has one provider of
it: its own proxy. That proxy runs on the machine's own network and binds the two public ports
directly. It declares no port mapping, so a node's `ports` setting cannot move it
([ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md) moves a
*published mapping*). The predecessor's proxy holds those ports and serves every public name on
the machine. The two cannot run at once.
So there is a circle:
- a web module cannot be taken until the mesh's proxy runs;
- the mesh's proxy cannot run until the predecessor's stops;
- when the predecessor's stops, **every name it serves goes dark** — because the mesh's proxy
serves only what mesh modules contributed, and the predecessor's services are not modules yet.
Adoption was built for exactly this shape and does not cover it. It keeps a **file** as it was and
a **container** as it was, holding each until its module is taken. It has nothing to say about a
service whose successor is *different software*: the predecessor's proxy and the mesh's read
different configuration and share no container name, so there is nothing to hold — only something
to replace, all at once, for every name at the same moment.
## Why it matters beyond this instance
The proxy is the first case, not the only one. Any provision with exactly one provider that owns a
scarce machine-wide resource has this shape: a resolver on port 53, a mail relay on 25, a
certificate authority. The migration's rule — one service at a time, each when its data has moved
— holds only while the successor can stand beside the predecessor. Where it cannot, the mesh
currently offers no way to take one step at a time.
It also decides the migration's order. The runbook had the proxy last, as the tidy final cutover.
It has to be first, because everything reachable by name waits on it.
## Open questions
- Should the mesh's proxy be able to serve routes it did not derive — a predecessor's names, given
to it as the operator's own configuration, each disappearing when the module that owns that name
contributes its route? That keeps the predecessor's configuration alive under the mesh's proxy,
which is what adoption promises everywhere else.
- Or should a provision be satisfiable by an **adapter**: a module that provides `route` by writing
into the predecessor's proxy, so modules migrate one at a time while the predecessor still
serves, and the proxy itself is taken last?
- What becomes of the certificates in either case? The predecessor holds them; the successor would
otherwise ask a public authority for every name at the moment of the cutover.
- Is there a general rule here for "a requirement whose only provider owns a scarce port", or is
each such provision its own decision?