Files
hq/04-ISSUES/122-a-module-cannot-ask-for-its-own-public-name/00-report.md
T
jochen d7078061ea Issue 122: a module cannot ask for its own public name
Three open module changes independently wrote one mesh's names into the catalogue — two literal
public URLs, because the software generates absolute URLs behind a proxy, and one bucket renamed to
match what exists here. None was careless: the mesh composes <label>.<public-domain> for the proxy
and never hands it back to the module that asked for the route, and no interpolation yields a public
name, so writing the answer down is the only expressible option.

Files it rather than blocking the three, because the fix is a mechanism and the instances are live
needs. The cost is stated: a second mesh installing the identity provider gets the first mesh's
hostname, and nothing distinguishes a literal domain from a version number.
2026-09-26 14:58:18 +02:00

4.0 KiB

status, opened, located-in, fixed-by, amended-design
status opened located-in fixed-by amended-design
located 2026-09-26
mesh-controller internal/catalogue/declaration.go
mesh-catalog modules/keycloak
mesh-catalog modules/minio
mesh-catalog modules/nextcloud

122 — A module cannot ask for its own public name, so three manifests wrote this mesh's names into the catalogue

What was observed

Reviewing the open module changes on 2026-09-26, three of them independently put a name belonging to one particular mesh into a module definition, each for a different piece of software and each as the only way to make the software work:

  • The identity provider's manifest gains KC_HOSTNAME: https://<label>.<public-domain> as a literal, because the software generates absolute URLs and, behind a proxy, cannot derive them.
  • The object store's manifest gains MINIO_BROWSER_REDIRECT_URL: https://<label>.<public-domain> as a literal, for the same reason — its console redirects to an absolute URL.
  • The file-sync module's S3 bucket is renamed from nextcloud to a name carrying this mesh's own name, so the module matches a bucket that already exists here.

No reviewer introduced these carelessly: each is the value the software needs, and there is nowhere else to put it.

What the mesh offers instead, and why none of it answers

A manifest may interpolate ${secret:…}, ${seat:…}, ${bound:…}, ${port:…} and ${machine:…}. The machine form resolves at, name and address — the machine's identity on the private network. None of them yields a public name.

The mesh does compose public names: a route contribution's label is joined to the node's public domain, <label>.<public-domain>, and the mesh interprets neither half. That happens inside the controller when a declaration is built, and the result reaches the proxy that serves the route. It does not reach the module that asked for the route, so a module that must tell its own software what it will be reached at cannot read what the mesh already worked out.

So the workaround is the only expressible option: write the answer down, in the definition, for the one mesh it is true of.

Why it matters beyond these three

A module definition is meant to hold what is true of the module everywhere, with everything particular to an installation resolved at assignment — the argument ADR 0112 (proposed) makes in full, and what issue 119 found for paths. A hostname is the same kind of fact as a path, and arriving by the same route: not because anyone disagreed with the principle, but because the mechanism that would honour it does not exist yet.

The cost is concrete. A second mesh installing the identity provider from this catalogue gets the first mesh's hostname, and its login flow breaks in a way that looks like a proxy fault. Nothing checks for it: a literal hostname in an env value is a well-formed string, and no rule distinguishes it from a version number.

The bucket case is the same shape with a different subject — an adopted resource's real name is also particular to one installation, and also has nowhere to live but the definition.

Open questions

  • Should a module be able to name what it will be reached at — a route-scoped interpolation resolving to the composed public name of a route it contributes, so the answer the mesh already computes is the one the software is told? That is the smallest fix and it stays within the existing vocabulary.
  • Does the scheme belong to it? Every instance here wrote https://, which is true of a route the proxy terminates and not of the module's own port.
  • For an adopted resource such as an existing bucket: is that a setting on the assignment (where ADR 0112 would put it), and if so what reads it — the provisioner, or the module's own values?
  • What check would notice the next one? A definition naming a public domain is detectable in the shape of the value, which is more than nothing, and less than a rule.