--- topic: what runs on it status: accepted date: 2026-09-04 deciders: jochen reconstructed: false extends: 0027-a-provision-names-what-the-consumer-is-coupled-to.md --- # 44. A public name is provisioned, not registered by hand ## Context The mesh names and resolves its own machines internally: the overlay generates `..` 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 0027](0027-a-provision-names-what-the-consumer-is-coupled-to.md)): 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..record.created` and `module..record.removed` ([ADR 0041](0041-events-are-a-relationship.md)), 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 0045](0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md)), `route` routes the host to the module, and `public-dns` makes the host resolve. ## References - [ADR 0027](0027-a-provision-names-what-the-consumer-is-coupled-to.md) — a capability is provisioned on declaration; a public name is one. - [ADR 0045](0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md) — the firewall, the other half of the reachability question this was asked with. - [ADR 0041](0041-events-are-a-relationship.md) — the provider's record events. - [ADR 0040](0040-what-a-module-is.md) — a provider and its interface; the neutral-interface, scoped-provider naming this follows.