Merge pull request 'Issue 092: genesis publishes to a registry the container runtime does not yet trust' (#79) from issue/092-genesis-registry-trust into main

This commit was merged in pull request #79.
This commit is contained in:
2026-09-23 23:38:46 +00:00
@@ -0,0 +1,77 @@
---
status: open
opened: 2026-09-22
located-in: []
fixed-by:
amended-design:
---
# 092 — Genesis publishes to a registry the container runtime does not yet trust
## What was observed
On the first real genesis, on a machine in use, 2026-09-22.
Genesis raises the mesh's registry, then pushes the control plane's image into it so that the
control plane can be reinstalled as an ordinary module pinned to a digest. The push failed:
```
the container runtime would not push <address>/mesh-controller:genesis:
Get "https://<address>/v2/": http: server gave HTTP response to HTTPS client
```
The mesh's registry speaks plain HTTP, deliberately: every path to it is inside the private
network's encryption ([ADR 0082](../../02-DECISIONS/0082-the-registry-is-reached-by-name-and-trusted-by-the-overlay.md)).
The container runtime refuses plain HTTP to a registry it has not been told to trust, and **the
thing that tells it is the networking module**, which genesis installs at step 16 — six steps
after the push.
So genesis cannot complete on a machine whose runtime was not already told, out of band, to trust
the address the mesh is about to serve its own images from.
The lab never sees it: its base image writes that trust into the runtime's configuration before
any bed starts. That is the same blind spot [issue 084](../084-taking-networking-on-an-adopted-node-restarts-every-container/00-report.md)
names from the other side.
Worked around by hand on the machine: the mesh's registry added to the runtime's trusted list,
keeping every key already there, and the runtime **reloaded** rather than restarted, so nothing it
was running stopped ([ADR 0102](../../02-DECISIONS/0102-the-mesh-writes-into-a-shared-file-never-over-it.md)).
Genesis then continued.
**Then it happened twice more**, on the same machine, within the hour:
- **By name, not only by address.** Once the mesh's builder built a module, it pushed the result to
the registry by its **mesh name** and port. The runtime trusted the numeric address genesis was
given and nothing else, so the push failed the same way. The trust has to cover every name the
mesh will use for that registry, not the one the operator typed.
- **The mesh's own trust names the wrong port.** The networking module writes the registry's trust
into the runtime's configuration — correctly written *into* it, keeping what was there
([ADR 0102](../../02-DECISIONS/0102-the-mesh-writes-into-a-shared-file-never-over-it.md)) — and
the value it wrote names the registry's **default** port while the node was given another. So the
mesh trusts an address its registry does not serve. That is the same fault as
[issue 088](../088-the-forges-own-address-names-a-port-it-may-not-have/00-report.md): an address
with a port in it that does not follow the node's setting.
## Why it matters beyond this instance
Genesis is meant to take a machine from nothing to a mesh without anyone editing files on it. It
has one step that cannot work unless somebody already did. Every future first node meets this, not
only a node adopted beside a predecessor, and the order is what makes it so: the trust the push
needs is installed after the push.
It also breaks the rule that each step says what it needs before it changes anything. Preflight
checks that the bundle's registries are reachable. It does not check that the runtime will speak
to the one the mesh is about to raise.
## Open questions
- Should genesis write the runtime's trust itself, before the publish step, the way it writes
everything else the foundation needs — and hand it to the networking module afterwards?
- Or should the push not need it: a registry served over TLS from the start, or an image loaded
rather than pushed?
- Should preflight refuse a genesis whose runtime does not trust the registry it is being given,
rather than letting it fail at step 10?
- Whatever writes that trust, which names of the registry must it cover: the address given, the
mesh name, loopback? The builder used the mesh name without being told to.
- Should the registry's trust be derived from what the registry module *serves* on that node,
rather than from its default port?