0066 was proven on a four-node bed before it was ratified: one node setting moved an entire domain, a routed name resolved inside the mesh and was issued a certificate by the internal authority, and TLS verified against that authority with no override. 08-connectivity rests on it and could not while it was proposed. 0067 records what deleting the lab's registry exposed — that pinning quietly required a registry before the thing that lets a mesh have a registry could start — and the pivot that resolves it. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
116 lines
7.2 KiB
Markdown
116 lines
7.2 KiB
Markdown
---
|
|
topic: the tiers
|
|
status: accepted
|
|
date: 2026-09-09
|
|
deciders: jochen
|
|
reconstructed: false
|
|
extends: 0007-connectivity.md
|
|
---
|
|
|
|
# 66. Public routing is name-agnostic, its names are resolved inside the mesh, and an internal authority can certify them
|
|
|
|
## Context
|
|
|
|
**[ADR 0007](0007-connectivity.md) and [connectivity §3](../03-DESIGN/01-to-be/08-connectivity.md)
|
|
made a public route a grant: a workload that must be reachable requires a route, the proxy provides
|
|
it, the consumer contributes the name it wants and the port it listens on.** What was never pinned
|
|
is **what that name is** — and building a whole mesh in the lab showed the gap costs more than it
|
|
looks.
|
|
|
|
**The catalogue shipped each route as a full domain.** A module that needed a public name carried
|
|
that name, in full, as a literal in its manifest. Running the same catalogue against a different
|
|
domain — a lab standing in for production, or a second operator's mesh — meant overriding that
|
|
literal on every routed module, per node. The mesh was, in effect, carrying a **map of names to
|
|
services**: the one thing it should never hold, because a name is the operator's choice (one runs
|
|
the forge at `git`, another at `code`) and the domain is the node's, and neither is the mesh's to
|
|
know.
|
|
|
|
**And a second gap surfaced the moment an internal issuer tried to certify those names.**
|
|
[Connectivity §5](../03-DESIGN/01-to-be/08-connectivity.md) already states the issuer must be
|
|
configurable and that the lab runs its own ACME authority. With that authority wired to the proxy,
|
|
issuance still could not complete: the authority accepted the order and offered a challenge, then
|
|
**could not connect to the validation target.** Nothing inside the mesh resolved the public route
|
|
name. The mesh publishes each `<node>.internal` name into every container, but not the public names
|
|
the proxy serves — so a validator living in the mesh had no address to reach, and a name the mesh
|
|
cannot resolve is a name it cannot have certified.
|
|
|
|
**The two are one problem.** A name the mesh can *compose* from parts it is given, and *propagate*
|
|
to whoever needs to resolve it, is exactly a name it can also have *certified* — and the reverse:
|
|
without the composition and the propagation, neither the routing nor the certificate is the
|
|
operator's to move between meshes.
|
|
|
|
## Considered Options
|
|
|
|
**1. Keep the full domain in the manifest, override per node.** The status quo. It works, and it is
|
|
wrong in the specific way this repository cares about: the catalogue holds a domain map, lab and
|
|
production differ by an override on every routed module rather than one fact, and a module manifest
|
|
names something — the public domain — that belongs to the node, not the module. An unowned name in
|
|
the wrong place is the shape of a leak.
|
|
|
|
**2. A module declares a label; the node declares its public domain; the mesh composes.** The route
|
|
contribution carries a subdomain the operator chose, the node carries its public domain as
|
|
node-level configuration, and the mesh joins `<label>.<public-domain>` and grants exactly that. The
|
|
mesh interprets nothing. Lab-versus-production becomes one node setting. Chosen.
|
|
|
|
**3. For certification, issue only from a publicly reachable node against a public authority.**
|
|
This is already true for public meshes and stays true. It is not an option for a lab or an
|
|
internal-only mesh: there is no public authority to answer, and no public reachability to validate
|
|
against. An internal issuer is required there — and an internal issuer must be able to *validate*,
|
|
which it cannot do unless the routed name resolves and is reachable **inside** the mesh. So the
|
|
resolution gap is not optional to close; it is what makes an internal authority possible at all.
|
|
|
|
## Decision
|
|
|
|
**The mesh core holds no map of hostnames, subdomains or domains.** A route contribution carries a
|
|
**label** (the subdomain) chosen by the module's operator. A node contributes its **public domain**
|
|
as node-level configuration. The mesh composes `<label>.<public-domain>`, grants exactly that name,
|
|
and never interprets what it means. One operator's forge at `git.example.tld` and another's at
|
|
`code.other.example` are the same module with two facts supplied around it.
|
|
|
|
**When the proxy is granted a name, the mesh publishes that name → the node that serves it into
|
|
internal resolution, mesh-wide** — the same mechanism, and the same "given by the mesh, not chosen
|
|
by a module," that already writes `<node>.internal` into every declared container
|
|
([connectivity §2](../03-DESIGN/01-to-be/08-connectivity.md)). The mesh propagates the names it was
|
|
told to serve. It still knows nothing about what any of them mean.
|
|
|
|
**An internal authority certifies those names by the same path a public one would.** The proxy is
|
|
pointed at whichever issuer the mesh names — a public ACME authority, or an internal one — and
|
|
trusts that issuer's root; nothing else about issuance changes. The internal authority validates by
|
|
reaching the routed name, which the clause above has just made resolvable inside the mesh. So the
|
|
three are one decision: **compose the name, propagate it, certify it** — each is meaningless without
|
|
the one before it.
|
|
|
|
## Consequences
|
|
|
|
- **Lab-versus-production is one node-level `public-domain` setting**, not an override on every
|
|
routed module. The same catalogue runs against any domain.
|
|
- **The manifest layer needs composition it does not yet have.** Today a route name is stored as a
|
|
literal, with no interpolation of a node's domain into a module's label. Until that exists, the
|
|
composed name is produced by a per-node settings override — a stopgap that reproduces option 1's
|
|
per-module cost and is explicitly *not* the design.
|
|
- **An internal issuer depends on route-name resolution.** Its challenge validates against the
|
|
routed name; without that name in internal resolution, issuance for it cannot complete inside the
|
|
mesh. The lab found this as a live failure, not a theory.
|
|
- **Nothing about the public path changes.** A publicly reachable node issuing a public name from a
|
|
public authority is untouched; this widens the same shape to names and meshes that are not public.
|
|
|
|
**How each is checked** — an unenforced rule is indistinguishable from a wrong one:
|
|
|
|
- **Name-agnostic:** the same catalogue resolves against two different public domains by changing
|
|
one node setting and nothing else; and no module manifest contains a full public domain. A
|
|
manifest that pins an FQDN is the smell the check looks for.
|
|
- **Resolution:** a request to a routed name, made from inside the mesh, reaches the workload that
|
|
serves it — and, the sharper check, issuance for that name against the internal authority
|
|
completes, which it cannot unless the validator resolved and reached the target.
|
|
- **Internal authority:** a TLS handshake to a routed name verifies against the internal root and
|
|
nothing else, the same shape §5 already uses for internal node-to-node names.
|
|
|
|
## References
|
|
|
|
- [ADR 0007 — connectivity](0007-connectivity.md), which made a route a grant and named exposure,
|
|
resolution and certificates as one context.
|
|
- [ADR 0009 — modules and the graph](0009-modules-and-the-graph.md), the provide/require/contribute
|
|
vocabulary a route and an authority both use.
|
|
- [Connectivity design §2, §3, §5](../03-DESIGN/01-to-be/08-connectivity.md), amended alongside this
|
|
record.
|