81 lines
4.5 KiB
Markdown
81 lines
4.5 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-10-01
|
|
located-in: [mesh-controller examples/route-proxy/main.go (routesFrom requires a route's public `name` and treats `internal-name` only as an alias of it; the handler serves every routed name to any source), mesh-controller internal/broker/membership.go (a membership says nothing of what its module receives or who the mesh is)]
|
|
fixed-by: mesh-controller PR 207 (the membership carries what a module receives and who the mesh is; the proxy follows it and serves internal names to the mesh only), mesh-catalog PR 211 (the proxy's bus account), mesh-controller PR 208 (the issue verb that delivers it), live 2026-10-02
|
|
amended-design: [03-DESIGN/01-to-be/08-connectivity.md, 03-DESIGN/01-to-be/25-the-bus-on-nats.md]
|
|
---
|
|
|
|
# 191 — A route with only an internal name is dropped as naming nothing
|
|
|
|
## What was observed
|
|
|
|
A module whose endpoint reaches only the private network could not be reached by its internal name.
|
|
The module ran and answered on its own port. Its route's internal name resolved to the serving node.
|
|
The request failed during the TLS handshake:
|
|
|
|
```
|
|
http: TLS handshake error from …: no public route for "unifi.home-server.internal" in this mesh,
|
|
so no certificate is asked for
|
|
```
|
|
|
|
The proxy's own log said why, every time it re-read its routes:
|
|
|
|
```
|
|
unifi on home-server asked for a route and named nothing; skipped
|
|
```
|
|
|
|
The route it skipped was not empty. The mesh had given it an endpoint, a port, a scheme and an internal
|
|
name, and no public name:
|
|
|
|
| route | `name` | `internal-name` | served |
|
|
|---|---|---|---|
|
|
| home-assistant | a public name | `home-assistant.home-server.internal` | under both |
|
|
| unifi | — | `unifi.home-server.internal` | under neither |
|
|
|
|
Three other modules on the same node were skipped with the same line on the same pass.
|
|
|
|
## Why it matters
|
|
|
|
**Reach is decided in one place, and the proxy reads the old shape of the decision.**
|
|
[ADR 0138](../../02-DECISIONS/0138-an-assignment-binds-an-endpoint-and-says-how-far-it-reaches.md)
|
|
made an endpoint's reach decide which names exist. The controller composes the public name, the
|
|
internal name, or both, and composes no name that nobody asked for
|
|
([issue 140](../140-an-endpoints-reach-is-not-declared/01-resolution.md)). An endpoint that reaches
|
|
only the private network is the ordinary case for anything that should not face the internet. It is
|
|
exactly the case the proxy drops.
|
|
|
|
**The failure is quiet and points the wrong way.** Nothing marks the module unhealthy. The handshake
|
|
error says *no public route*, which reads as a certificate fault on the reader's side. The line that
|
|
gives the real cause is one of four identical lines repeated every few seconds in a log nobody reads
|
|
until they already suspect the proxy.
|
|
|
|
**The opposite move is not a workaround.** Giving the endpoint public reach makes the proxy serve it.
|
|
It also publishes an administration interface to the internet to get a name on the private network.
|
|
|
|
## Open questions
|
|
|
|
- Should the proxy refuse a route it cannot serve in a way the controller or an operator sees, not
|
|
only in its own log? The same silent skip covers a route with no usable port or an unknown scheme.
|
|
- What checks that what the controller composes and what the proxy serves stay the same shape? ADR
|
|
0138 changed one side and nothing failed on the other.
|
|
|
|
## Resolved (2026-10-02)
|
|
|
|
Built as [ADR 0167](../../02-DECISIONS/0167-a-membership-carries-what-its-module-receives-and-who-the-mesh-is.md)
|
|
decided, and live on both machines that run the proxy. Each logs that its routes now come from its
|
|
membership, and serves internal names to the four machines the mesh names. Checked by hand:
|
|
|
|
- the internal-only route answers through the proxy from the serving machine and from two other
|
|
machines of the mesh, over a certificate from the mesh's own authority that each verifies;
|
|
- the same name asked from an address outside the mesh is answered as a name never routed, over plain
|
|
HTTP, and refused in the TLS handshake; the list of served names it is shown leaves out every internal
|
|
name.
|
|
|
|
Two things the rollout found are their own records: the proxy's bus account could be issued only from
|
|
the controller's command line, until mesh-controller PR 208 added the `issue` verb, and the status line
|
|
counting every module as a bus user without a credential is
|
|
[issue 195](../195-every-assigned-module-is-counted-as-a-bus-user-without-a-credential/00-report.md).
|
|
The serving machine also lacked the certificate-trust module, so it could not verify the mesh's own
|
|
certificates until it was assigned there.
|