0049: a public name is provisioned like any capability — a module requires public-dns and contributes its host; a neutral interface answered by registrar-scoped providers (cloudflare-dns, route53-dns) that create/remove the record pointing the name at the mesh's public ingress. Pairs with route (the proxy) and a public cert (the proxy's ACME). 0050: answers the firewall question. The firewall is NOT a provider like the proxy — it is a machine's own filter, derived by the host as the sum of what its modules declare they listen on, with 'from' the whole of public-vs-internal. Enforced both ways, unknown keys refused — closing 04-ISSUES/003. A public service is exposed through the proxy (listens from:mesh + requires route), not by opening its own port. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
5.1 KiB
status, date, deciders, reconstructed, extends
| status | date | deciders | reconstructed | extends |
|---|---|---|---|---|
| proposed | 2026-09-04 | jochen | false | 0005-capabilities-are-provisioned-on-declaration.md |
49. A public name is provisioned, not registered by hand
Context
The mesh names and resolves its own machines internally: the overlay generates
<service>.<node>.<suffix> wildcards, dnsmasq answers them (wildcard-resolution), and the mesh
issues a certificate for each internal name. A service reachable at a public domain —
plex.example.com, not plex.anchor.internal — needs three things that machinery does not give it:
- a public DNS record at a registrar or DNS provider, so the name resolves on the internet;
- a publicly-trusted certificate for it, because the mesh's own authority is trusted by nobody outside the mesh;
- and routing from that name to the module — which the reverse proxy already does: a module
requirestheroutecapability and the proxy provides it, routing by the host it was asked for.
The routing exists. The public DNS record does not: the mesh has no way to make a name resolve on the public internet, so today that is a step someone does by hand at a DNS provider, outside the mesh, remembered nowhere. A public name is therefore the one part of reaching a service that the declaration graph cannot grant or withdraw — which means it is created once and outlives whatever it was for, the shape of drift this project exists to remove.
Decision
A public name is a capability, requested like any other
A module reachable at a public host declares requires: ["public-dns"] and contributes the hostname
it wants — beside requires: ["route"], which exposes it through the proxy. The name is then
provisioned on declaration (ADR 0005): created
when the module is assigned, removed when it is withdrawn, reconciled like every provision.
The interface is neutral; the providers are the registrars
public-dns is drawn at the consumer's coupling: the consumer wants a public name that resolves to
me, and does not care whether Cloudflare, Route 53 or a registrar's own API puts the record there.
So the interface is neutral and the providers are provider-scoped — cloudflare-dns,
route53-dns, porkbun-dns — each implementing the one public-dns contract, the same way a
neutral database coupling is answered by postgres-database and mssql-database. A module names
public-dns; it never names a registrar.
The record points at the mesh's public ingress, not at the node
What the name resolves to is the address the reverse proxy answers on, not the consuming machine's.
A public service is reachable only through the proxy — the proxy holds the route grant and routes
by host to the module — so the public name must resolve to the proxy. public-dns and route are
the two halves of one public exposure: the name, and what the name reaches.
The record is a fact, not a secret
A DNS record is public by definition, so the grant returns the fully-qualified name and its TTL and nothing sealed. The only secret is the provider's own API credential, which is the provider module's own-secret and never leaves it — the module that wanted the name never sees it.
Events
The provider emits module.<provider>.record.created and module.<provider>.record.removed
(ADR 0046), so which names the mesh publishes, and where is a
question answered from the event trail and the grants, not from a folder of records edited at a
provider.
The public certificate is the proxy's, and is named here only to pair it
A public name without a publicly-trusted certificate is reachable and not trusted — the same pairing the internal name and the mesh-issued certificate already have. Obtaining that certificate (ACME against the now-resolving public name) is the reverse proxy's to do, and its mechanism is its own decision; it is named here so the pairing is not forgotten, not resolved here.
Consequences
- A public name is created and torn down with the module, so it cannot outlive it, and the mesh can say which public names it publishes without anyone reading a registrar's dashboard.
- Adding a registrar is adding a provider that answers
public-dns; the modules that want names do not change. - Public exposure of a service is a trio of separate, declared, enforced relationships: the firewall
opens the proxy's public port (ADR 0050),
routeroutes the host to the module, andpublic-dnsmakes the host resolve.
References
- ADR 0005 — a capability is provisioned on declaration; a public name is one.
- ADR 0050 — the firewall, the other half of the reachability question this was asked with.
- ADR 0046 — the provider's record events.
- ADR 0045 — a provider and its interface; the neutral-interface, scoped-provider naming this follows.