3.7 KiB
status, opened, located-in, fixed-by, amended-design
| status | opened | located-in | fixed-by | amended-design | ||
|---|---|---|---|---|---|---|
| located | 2026-09-22 |
|
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 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
routeby 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?
Decided
2026-09-22. ADR 0104: during a migration, a provision whose only provider owns a scarce machine-wide resource may be answered by an adapter that writes into the predecessor's own configuration. For the route, the predecessor's proxy keeps serving every name and keeps its certificates, while each migrated module's name is pointed at the mesh's container. The proxy is the last cutover again, and by then every route is one the mesh contributed.