Two birth-address outages and a registry that would have been the third; a container that keeps a stale environment after its file changes; a host command that applied a converged declaration to an adopted node; the hub and the vault without seats. And the decision the operator made under it all: the hub adopts the predecessor's tunnel in place, key and peers and range and port.
3.6 KiB
status, opened, located-in, fixed-by, amended-design
| status | opened | located-in | fixed-by | amended-design | |||
|---|---|---|---|---|---|---|---|
| located | 2026-09-23 |
|
102 — An address recorded at genesis or at build does not follow the node's ports
What was observed
The control-node, 2026-09-23, migrating the foundation's store and broker onto the ports the predecessor served them on — the move ADR 0100 describes: the ports given become that node's settings, and every place that uses them reads them from there.
Most places did. When the store was given 6852, the bindings handed to the forge, the analytics service and the catalogue all said 6852. When the broker was given 5679, its consumers reconnected.
Three places did not, and each took the mesh down in a different way:
- The controller's own store address. A secret written at genesis says
127.0.0.1:5432. The store moved; the controller could not reach its inventory; the mesh was headless. - The controller's own broker address. The same, for
127.0.0.1:5672. The controller crash-looped and its control queue filled. - Every image reference the mesh has built. Each recorded build reads
<registry-address>:5100/<module>/<artifact>@sha256:…, and declarations carry that literal. Move the registry and every fresh pull of a mesh image fails — a new node, a recreate after eviction. Found by reading before the move; the first two were found by making it.
All three are the same fact: an address was written down when the port was decided, rather than read from the node's settings when it is used. The first two live in genesis secrets; the third lives in stored data, which is worse — it is baked into every build ever recorded.
Both outages were closed by hand with a forwarder on the old address to the new one. Two such forwarders are holding the control-node's mesh together while this is open. They are not the fix; they are the shape of the bug, made visible.
Why it matters beyond this instance
A port that is a setting in nine places and a constant in three is a constant. The migration's whole method — take the predecessor's ports one service at a time — depends on every reader following the setting, and the readers that do not are exactly the control plane's own, which is the worst place for them: the failure is headlessness, and headlessness cannot be repaired through the mesh.
The image reference case will bite any mesh that changes its registry's port, or moves its registry to another node, or ever has two registries. It also means an image is recorded by where it was pushed rather than what it is: the digest is the identity, the address is a route to it, and the mesh stores them as one string.
This is the same hole issue 085 found for the packages port, fixed there for that one reader. It is not one reader; it is a category.
Open questions
- Should the controller read its own store and broker addresses from the node's settings at start, the way it composes them for every other module — and re-read them when they change, since it is the thing that changes them?
- Should a recorded build store the artifact's digest and path only, with the registry address composed into the declaration from the node's current settings — so a reference is assembled where it is used, never stored?
- Is there a way to find the remaining constants mechanically — every place a foundation port number appears as a literal — rather than one outage at a time?