Files
hq/02-DECISIONS/0104-a-provision-may-be-answered-by-an-adapter-to-the-predecessor.md
T

94 lines
5.1 KiB
Markdown

---
topic: the mesh
status: accepted
date: 2026-09-22
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md
---
# 104. A provision may be answered by an adapter to the predecessor
## Context
[Issue 093](../04-ISSUES/093-the-successor-proxy-cannot-serve-what-the-predecessor-still-serves/00-report.md),
found on the first adopted node hours after it was raised. Every module reachable by name requires
a **route**. The mesh has one provider of it, its own proxy, which binds the two public ports on
the machine's own network. The predecessor's proxy holds those ports and serves every public name
there. So:
- a web module cannot be taken until the mesh's proxy runs;
- the mesh's proxy cannot run until the predecessor's stops;
- and when it stops, every name the predecessor served goes dark, because the mesh's proxy serves
only what mesh modules have contributed.
Adoption ([ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md)) keeps a *file* as it
was and a *container* as it was. It has nothing for a service whose successor is **different
software**: nothing shares a name to hold, so there is only replacement, of every name at once.
The certificates decide the rest. The predecessor's proxy holds them and renews them. A successor
taking over first would have to obtain a certificate for every public name in the same window in
which it took the ports — with nothing else migrated yet, and the only way back being to start the
predecessor again.
## Considered Options
1. **The successor proxy serves routes it did not derive**, given as the operator's configuration,
each disappearing as the module owning that name contributes its own. Rejected for the
migration: it puts the riskiest step first, and it puts configuration the mesh does not own
into the one component whose design is that the mesh derives its configuration.
2. **Take the proxy first and accept the window.** Rejected: every public name at once, before
anything else has moved, with certificates to obtain in the same window.
3. **Make the requirement optional**, so a module can be taken while nothing provides route.
Rejected: it is not optional. A module that says it must be reachable and is not is a module
reporting success while serving nobody.
4. **An adapter module provides the provision by writing into the predecessor's service.** Adopted.
## Decision
**A provision whose only provider owns a scarce machine-wide resource may be answered, during a
migration, by an adapter module that writes into the predecessor's own configuration.**
For the route: a module that **provides `route`**, receives every contribution as the proxy does,
and writes each one as a route file where the predecessor's proxy reads its configuration —
pointing at the machine port the contributing module now publishes. The predecessor's proxy keeps
serving every name it already serves, keeps its certificates and keeps renewing them; a name whose
module has migrated is served by the same proxy, pointing at the mesh's container instead of the
predecessor's.
**It is migration scaffolding and says so.** It is assigned only on an adopted node, it writes only
files it can name as its own, and it is removed when the predecessor's proxy retires — at which
point the mesh's own proxy takes the ports and already knows every route, because by then every
name belongs to a module that contributes it.
**This does not make the mesh the predecessor's manager.** The adapter writes route files and
nothing else, and only because the predecessor's control has been stopped by the operator, which
ADR 0100 requires before a node is adopted.
## Consequences
- The migration keeps its shape: one service at a time, each reversible on its own. The proxy is
the **last** cutover again, not the first.
- There is no certificate event until the end, and at the end there is one, for a proxy that
already has every route from the mesh.
- The mesh writes into a directory the predecessor owns. That is safe only while the predecessor's
control is stopped, and it is why the adapter is refused on a converged node.
- The adapter is throwaway code with a stated end. Something has to delete it; that something is
the operator, and the flip is the moment.
- A second provision with this shape — a resolver on 53, a mail relay on 25 — now has a pattern to
follow rather than a new argument to have.
## How it is checked
Unit tests hold the adapter to writing one route file per contribution, naming each file as its
own, removing a file when its contribution goes, and leaving every file it did not write. The
controller is held to resolving `route` from it exactly as from the proxy. The adoption lab bed
gains a step: with the predecessor's proxy serving a name, a module taken behind the adapter is
reachable under that name, and the predecessor's own names keep answering. Assigning the adapter
to a converged node is refused.
## References
- [Issue 093](../04-ISSUES/093-the-successor-proxy-cannot-serve-what-the-predecessor-still-serves/00-report.md)
- [ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md), [ADR 0007](0007-connectivity.md)