ADR 0056 + connectivity: public routing is name-agnostic, resolved in-mesh, internally certifiable
A route contribution carries a label; the node carries its public domain; the mesh composes <label>.<public-domain> and holds no name map. A granted route is published into internal resolution so anything in-mesh (notably an internal ACME authority) can resolve and reach it. That authority certifies routed names by the same path a public one would, differing only in issuer and trusted root. Records the decision as proposed and amends connectivity SS2/SS3/SS5 plus its Open list with the lab findings behind it. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -7,7 +7,7 @@ code:
|
||||
- mesh-control internal/identity/authority.go
|
||||
- mesh-host internal/identity/serving.go
|
||||
- mesh-host internal/apply (the service that reflects a rule set)
|
||||
updated: 2026-08-31
|
||||
updated: 2026-09-09
|
||||
decisions:
|
||||
- 02-DECISIONS/0005-the-node-host.md
|
||||
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
|
||||
@@ -16,6 +16,7 @@ decisions:
|
||||
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
|
||||
- 02-DECISIONS/0007-connectivity.md
|
||||
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
|
||||
- 02-DECISIONS/0056-public-routing-is-name-agnostic.md
|
||||
---
|
||||
|
||||
# Connectivity
|
||||
@@ -343,6 +344,27 @@ that module and nothing else.
|
||||
the argument for the table in ADR 0009 being a table: the pattern is only obvious once seen, and
|
||||
the cost of not seeing it is inventing a mechanism that already exists.
|
||||
|
||||
### And the public names a proxy serves must resolve in the mesh too
|
||||
|
||||
*2026-09-09, found by an internal certificate authority that could not issue.* The mesh writes every
|
||||
`<node>.internal` name into every declared container and treats the public names a proxy serves as a
|
||||
separate matter — *what routes it once it arrives is a proxy's, and stays separate*, above. That
|
||||
holds for a client dialling by internal name. It does not hold for anything **inside** the mesh that
|
||||
must reach a public name, and the first such thing to appear was the internal issuer of §5.
|
||||
|
||||
**An issuer validates by connecting to the name it is certifying.** Asked for a certificate for a
|
||||
routed public name, the internal authority accepted the order, offered a challenge, and then could
|
||||
not connect: nothing in the mesh resolved that name, so the challenge had no target. A name the mesh
|
||||
can reach from the outside but cannot resolve from the inside is a name it cannot certify with an
|
||||
authority of its own.
|
||||
|
||||
**So a granted route is published into internal resolution as well** — the routed name to the node
|
||||
that serves it, mesh-wide, by the same mechanism that writes the node names. It is *given by the
|
||||
mesh, not chosen by a module*, for the same reason the node names are: a module listing the routes
|
||||
would go stale the day one changes. The mesh propagates the names it was told to serve and still
|
||||
knows nothing about what they mean
|
||||
([ADR 0056](../../02-DECISIONS/0056-public-routing-is-name-agnostic.md)).
|
||||
|
||||
## 3 — Exposure
|
||||
|
||||
Settled by [ADR 0007](../../02-DECISIONS/0007-connectivity.md); summarised here because
|
||||
@@ -389,6 +411,27 @@ the mesh to tell them apart.
|
||||
returning the workload's own answer, then by unassigning the module and requiring the same request
|
||||
to stop working.*
|
||||
|
||||
### The name is a label, not a domain
|
||||
|
||||
*2026-09-09, from running the whole mesh in the lab.* A route contribution carried its public name
|
||||
in full — the forge as `git.example.tld`, spelt out in the module. Pointing the same catalogue at a
|
||||
different domain — a lab standing in for production, or a second operator's mesh — meant rewriting
|
||||
that name on every routed module. The mesh was holding a **map of names to services**, which is the
|
||||
one thing it must not: a public name is two facts owned by two different places, and neither is the
|
||||
module's manifest.
|
||||
|
||||
**A module contributes a label; the node contributes its public domain; the mesh composes.** The
|
||||
operator chooses where the forge lives — `git`, or `code` — and that is the module's to say. The
|
||||
domain is the node's, set once. The mesh joins them and grants `<label>.<public-domain>`,
|
||||
interpreting neither half. Moving a mesh to another domain is one node setting, not an edit per
|
||||
module.
|
||||
|
||||
**Today the name is still a literal, and that is the gap.** There is no interpolation of a node's
|
||||
domain into a module's label, so the composition is done by a per-node override — which reproduces
|
||||
exactly the per-module cost it is meant to remove. The design is the composition; the override is a
|
||||
stopgap until the manifest layer can carry a label and a domain separately
|
||||
([ADR 0056](../../02-DECISIONS/0056-public-routing-is-name-agnostic.md)).
|
||||
|
||||
## 4 — Filtering
|
||||
|
||||
**Derived from what is assigned here, and from the overlay's shape** — a node's open ports are a
|
||||
@@ -539,6 +582,24 @@ generated, the other verifies against the mesh's authority and nothing else. Eve
|
||||
passed while the server could not start — the key was present, the certificate was valid, and
|
||||
nothing read either the way a server would.*
|
||||
|
||||
### An internal issuer, pointed at and trusted
|
||||
|
||||
*2026-09-09, from wiring one to the proxy in the lab.* §5 above asks for two things this build
|
||||
leaned on: the issuer must be configurable, and the lab runs its own ACME authority rather than
|
||||
collapsing the split. Wiring the proxy to that authority is the whole of it — the proxy is told
|
||||
**which** issuer to use and given that issuer's **root** to trust, and every other step of issuance
|
||||
is unchanged. **The same code path certifies against an internal authority as against a public one;
|
||||
only the issuer differs.** That is what makes trusted certificates possible for a mesh whose names
|
||||
the public internet cannot resolve.
|
||||
|
||||
**And it does not work until the routed name resolves inside the mesh** — the §2 finding above,
|
||||
arriving here because this is what needed it. The authority's challenge reaches the routed name only
|
||||
once that name is in internal resolution; a public authority is handed that dependency by public
|
||||
DNS, and an internal one has to be handed it by the mesh. *Checked by a handshake to a routed name
|
||||
that verifies against the internal root and nothing else — which cannot succeed unless the issuer
|
||||
first reached the name to certify it*
|
||||
([ADR 0056](../../02-DECISIONS/0056-public-routing-is-name-agnostic.md)).
|
||||
|
||||
## What this removes
|
||||
|
||||
The list is worth having in one place, because it is most of the argument:
|
||||
@@ -572,3 +633,12 @@ The list is worth having in one place, because it is most of the argument:
|
||||
it expressible; nothing here says the overlay or the resolver handle it.
|
||||
- **Reporting declared-versus-observed.** ADR 0007 makes the disagreement detectable and does not
|
||||
say who looks or what they are told.
|
||||
- **Composing a route name from a label and a node's domain.**
|
||||
[ADR 0056](../../02-DECISIONS/0056-public-routing-is-name-agnostic.md) makes the public name the
|
||||
operator's to move between meshes, but the manifest layer still stores it as a literal — so today
|
||||
the composition is a per-node override rather than the design. The interpolation that would let a
|
||||
module carry a label and a node carry the domain, and the mesh join them, does not yet exist.
|
||||
- **Publishing route names into internal resolution.** The same ADR requires a granted route to be
|
||||
resolvable inside the mesh, not only routable from outside it; the mechanism that writes
|
||||
`<node>.internal` into containers does not yet also write the routed names, which is why an
|
||||
internal issuer cannot currently validate one without a hand-placed entry.
|
||||
|
||||
Reference in New Issue
Block a user