Files
hq/02-DECISIONS/0082-the-registry-is-reached-by-name-and-trusted-by-the-overlay.md

5.0 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
building it accepted 2026-09-17 jochen false 0072-two-graphs-and-the-build-chain.md

82. The registry is reached by name, and the overlay is its security

Context

Delivery ends at a node pulling an image, and for every node but the one that built it, that step has never worked. Three facts conspired, each recorded separately:

  • Artifact references are written under 127.0.0.1:5000 — a deliberate parking (the catalogue says so in the commit that reverted the mesh-reachable name: "the runtime refused it: 'http: server gave HTTP response to HTTPS client' … the binding expression returns then"), so a joined node is told to pull from its own loopback (issue 048).
  • A container runtime treats any non-loopback registry as HTTPS, and the mesh's registry serves plain HTTP; nothing the mesh writes tells any runtime otherwise — the one place that file existed was the lab's, which is why this never failed in a bed (issue 048).
  • Nothing gives a node an account for the registry (issue 042) — and nothing has ever said whether one is required.

The tempting fix is TLS from the mesh's own authority. The machinery even exists — the mesh CA issues leaves for <node>.internal names, and a certificate: manifest field delivers them. But the registry is reached over the overlay, and the overlay is WireGuard: every byte is already encrypted and already authenticated to a peer the mesh admitted. The mesh's own code has carried this position for weeks: "the registry is reached over the mesh's own private network … a second layer inside it would be certificates to issue and rotate for no property the first does not have." TLS inside the tunnel would also re-order genesis (a certificate needs the network, the registry precedes it) and put CA handling into three clients (the runtime, the archive fetcher, the builder) — cost with no new property.

Decision

The overlay is the registry's transport security, and the mesh writes the trust it means.

  1. References name the registry by its mesh name. The builder publishes to <provider>.internal:<port> (the binding's at and port) — the parked one-line change lands. Genesis still publishes to loopback on the first machine, before any overlay exists; references minted at genesis stay loopback and are valid where they matter — on that machine. A rebuild re-pins to the mesh name.
  2. Every node on the private network is told the mesh's registry speaks plain HTTP. The controller injects, into every such node's declaration, a merged /etc/docker/daemon.json naming <provider>.internal:<port> under insecure-registries, and a service resource that restarts the runtime when that file changes — the /etc/hosts pattern for the content, the nftables pattern for the reload. No module author is involved; being on the network is what grants the trust, because being on the network is what the trust is.
  3. No accounts (issue 042), recorded as the position it always was. Reading and pushing require presence on the overlay and nothing else. The boundary is enforced, not assumed: the registry's listens is from: mesh, the firewall derives from it, and the overlay admits only peers holding mesh-issued keys. An operator's external registry credential is an ordinary operator-supplied secret (secret accept), owned by whichever module names that registry. Per-node accounts return as a decision, not a patch, if the boundary assumption ever changes — the shape would be the existing provision flow.

How this is checked

  • The no-fake lab bed: a joined node pulls a mesh-built image by the registry's .internal name over the overlay — an address outside every range the lab's own runtime configuration trusts, so the mesh-written trust is what makes it work or nothing does.
  • The firewall half is generated from listens and visible in the node's ruleset (from: mesh).
  • The plaintext-inside-tunnel position holds exactly as long as the registry is unreachable off the overlay; the listens stanza and the derived ruleset are the check.

Consequences

A runtime restart when the trust first lands on a node — at joining, before workloads, where it is free. The registry's HTTP is exposed to whatever stands on the overlay, which is the stated boundary; a mesh that wants defence in depth inside its own tunnel reopens this record rather than bolting certificates on quietly. Issues 042 and 048 close on this record.

References