From a4440c9acf0fdd48b4ed1df220f3e2336d0d9436 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 9 Sep 2026 23:06:58 +0200 Subject: [PATCH] =?UTF-8?q?Issue=20038=20resolved=20=E2=80=94=20corrected?= =?UTF-8?q?=20root=20cause=20(same-node=20served-port=20mismatch,=20not=20?= =?UTF-8?q?loopback)=20+=20fix=20reference?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The real defect is same-node consumers announced the declared port instead of the assigned/published one; the loopback observation was a stale pre-0038 build. Fixed in mesh-control fix/same-node-provider-announced-port (c147a26) with a regression test. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF --- .../00-report.md | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/04-ISSUES/038-a-provider-is-announced-at-a-name-its-port-is-not-bound-to/00-report.md b/04-ISSUES/038-a-provider-is-announced-at-a-name-its-port-is-not-bound-to/00-report.md index 9afa592..9228171 100644 --- a/04-ISSUES/038-a-provider-is-announced-at-a-name-its-port-is-not-bound-to/00-report.md +++ b/04-ISSUES/038-a-provider-is-announced-at-a-name-its-port-is-not-bound-to/00-report.md @@ -1,8 +1,8 @@ --- -status: open +status: resolved opened: 2026-09-09 -located-in: [] -fixed-by: +located-in: [mesh-control] +fixed-by: mesh-control — a same-node provider is announced at the port it is published on amended-design: --- @@ -49,24 +49,43 @@ consumers (the ordinary small-mesh case) is exactly where it bites. It also blocks anything that must *reach* a routed/served name from inside the mesh, not just application traffic — see the internal-CA validation dependency noted in the connectivity design. -## Narrowing +## Diagnosis -The loopback bind appears tied to **mesh-assigned** host ports, not to publishing as such. A -container that declares a bare port (`"5432"`) — which the mesh assigns a host port for — is bound -to `127.0.0.1:`. A container that declares an explicit host mapping (`"17672:15672"`) is -bound to `0.0.0.0:17672` and is reachable at the node's private-network address. So the defect is -in the path that assigns and binds a host port for a bare declaration, not in the general publish -step — which is also why a route to an explicitly-mapped port works while a database on an assigned -port does not. +The symptom's first reading — "published on loopback" — was **partly a red herring**. Two things +were tangled: -## Open questions +1. **The real, current-code defect is a served-*port* mismatch, not a bind address.** A bare + `ports: ["5432"]` is assigned a host port and published as `"15432:5432"` — no bind IP, so on + **all interfaces**, reachable at the node's private-network address. But the port a consumer is + *told* is only re-derived from the assignment on the **cross-node** path. The **same-node** paths + (the resolver's `servedHere`, and the `here()` fallback) settle their served facts *while + resolving* — before the host port is assigned — so they carry the **declared** port (5432), not + the **assigned** one (15432). A co-located consumer is therefore announced + `.internal:5432` while the provider is published on `.internal:15432`, and dials a + port nothing listens on. Cross-node consumers were always fine, which is why it read as "the + small-mesh case." -- Should a `from: mesh` provision publish on the node's private-network address specifically, on - all interfaces, or on whatever address the resolver will hand consumers as `at` — i.e. should the - publish bind be derived from the *same* decision that fills `at`, so the two cannot drift? -- What is correct for a `from: machine` provision by contrast — is loopback right there, and is the - bug simply that both are being treated the same? -- How is this checked so it cannot regress: a test that resolves a co-located provider/consumer and - asserts the announced `at:port` is actually connectable (not merely that a binding file exists, which - is what 018 asserts)? -- Does the same address/publish mismatch affect the substrate's own ports, or only module provisions? +2. **The `127.0.0.1:15432` seen in the running lab was a stale build.** Current code's publish step + binds all interfaces; the running instance was raised from a mesh-control predating the ADR 0038 + publish rewrite. The substrate's own store *is* deliberately `127.0.0.1:5432` (a private store + must not be exposed) — correct, and not this bug. + +## Fixed by + +`mesh-control` branch `fix/same-node-provider-announced-port` (`c147a26`): after the host port is +assigned, same-node needs (and the `here()` fallback) are redirected through the same +provision→module→assigned-port lookup the cross-node path already uses, so a co-located consumer is +announced the port that is actually published. Idempotent (keyed by the declared port). Regression +test `TestASameNodeProviderIsAnnouncedAtThePortItIsPublishedOn` asserts the announced port equals +the published host port for a co-located provider/consumer — the next assertion after 018's (which +only checked a binding file exists); verified failing without the change. + +*Not yet merged, and the running lab is additionally stale — proving it end-to-end there needs +mesh-control rebuilt and the affected consumer containers recreated.* + +## Noted, not taken + +Binding the assigned port to the node's private-network address specifically (rather than all +interfaces) would be defence-in-depth and would make the publish address match `at` by construction +— but it is a larger behavioural change entangled with the unenforced firewall scope +([003](../003-firewall-scope-is-read-by-no-code/00-report.md)), so it is left as an option.