Files
hq/02-DECISIONS/0049-a-public-name-is-provisioned-like-any-capability.md
T
jschoubben bfa08742f8 Consolidate hq: ADRs 0044-0052 merged in, and 0017/0049-0052 accepted
Brings the independent ADR branches (0044-0052) onto one branch so hq lands as a
single MR, and ratifies the five that were still proposed — 0017, and 0049-0052,
which are implemented and green in the lab. With 0053/0054 already accepted here, the
whole ADR chain 0044-0054 is accepted on this branch.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-05 03:01:06 +02:00

5.1 KiB

status, date, deciders, reconstructed, extends
status date deciders reconstructed extends
accepted 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 requires the route capability 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), route routes the host to the module, and public-dns makes 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.