diff --git a/04-ISSUES/094-a-port-published-as-the-machine-side-cannot-be-moved/00-report.md b/04-ISSUES/094-a-port-published-as-the-machine-side-cannot-be-moved/00-report.md new file mode 100644 index 0000000..f9cdd1f --- /dev/null +++ b/04-ISSUES/094-a-port-published-as-the-machine-side-cannot-be-moved/00-report.md @@ -0,0 +1,59 @@ +--- +status: located +opened: 2026-09-23 +located-in: [mesh-controller internal/catalogue/filtering.go, mesh-controller cmd/mesh-controller/plan.go] +fixed-by: +amended-design: +--- + +# 094 — A port published as the machine side of a mapping cannot be moved, and trying blocks every push + +## What was observed + +On the control-node, immediately after the first module was migrated, 2026-09-23. + +A module publishes two ports. One is written short, so the machine's side and the software's side +are the same number. The other is written long: **a machine port mapped to a different port inside +the container**, because the software's own port is one the machine's own daemon already holds. +The module's `listens` names the **machine** side of that mapping, deliberately, and says why. + +The predecessor served that port on a different machine port. Moving the module's to match — the +whole point of a port being a per-node setting +([ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md)) — was +refused: + +``` +gives port 2222 a machine port, and no container of its publishes 2222 — +the mesh cannot move a port the module does not publish +``` + +It does publish it. The check collects only the **last** segment of each published entry, which is +the container's inner port, so for a long-form mapping it sees the wrong half. Naming the inner +port instead is accepted and then never consulted, because the lookup is by what the module says +it listens on — which is the machine side. + +**And the refusal is not confined to the setting.** While that setting exists, composition fails, +so **every push to that node is refused** until it is removed. A setting that cannot work should +be refused where it is set, not where it is read. + +The same blind spot appears one step further on: on an adopted node **no opening was derived** for +that port either, and a per-node `expose` for it produced none, so the firewall never opened what +the module serves. + +## Why it matters beyond this instance + +Every module that maps a machine port to a different port inside its container has this hole, and +the pattern is common precisely where it matters: a service whose own port collides with something +the machine already runs. It is also exactly the case a migration hits, because matching the +predecessor's port is how a service keeps working when it changes hands. + +The push-blocking half is worse than the feature gap. One unusable setting stops a machine being +told anything at all, and the message names a port rather than the setting that caused it. + +## Open questions + +- Should a setting name the port the software listens on, the machine port, or either, and what + refuses the ambiguous case where a module publishes both halves of some other mapping? +- Should `settings set` validate against the module's manifest at the moment it is set, so an + impossible setting cannot be stored? +- Should composition fail the whole node, or fail that module and send the rest? diff --git a/04-ISSUES/095-a-module-assigned-after-genesis-has-no-broker-account/00-report.md b/04-ISSUES/095-a-module-assigned-after-genesis-has-no-broker-account/00-report.md new file mode 100644 index 0000000..b6eff84 --- /dev/null +++ b/04-ISSUES/095-a-module-assigned-after-genesis-has-no-broker-account/00-report.md @@ -0,0 +1,47 @@ +--- +status: located +opened: 2026-09-23 +located-in: [mesh-controller cmd/mesh-controller/modules.go] +fixed-by: +amended-design: +--- + +# 095 — A module assigned after genesis has no broker account, and finds out by restarting + +## What was observed + +On the control-node, during the first migration, 2026-09-23. + +A module was assigned and taken. Its service came up and served correctly. Its **sidecar** — the +part that speaks on the bus — restarted every few seconds with: + +``` +cannot read the broker credential at /run/secrets/broker: SyntaxError: Unexpected token 'V', +"VK-XiWBjPt"... is not valid JSON +``` + +The module declares an own-secret named `broker`, as every module on the bus does. The mesh had +minted it as **a random value**, the way it mints any own-secret, where the sidecar expects an +**account**: a url with credentials in it. + +The account is a separate, explicit act — `module issue --node ` — which genesis +performs for the foundation's own modules. Nothing performs it for a module assigned afterwards, +and nothing says it is needed. Running it delivered a real account and the sidecar came up. + +## Why it matters beyond this instance + +Every module with a sidecar will be assigned after genesis, because that is what migrating a +service is. The failure is late, and it looks like a code fault in the module rather than a +missing step: the service works, and only the half that talks to the mesh crashes. + +Two mechanisms share one name here. `own-secrets: {broker: …}` means *give me a place to put my +broker credential*, and the minting path treats it as *mint me a secret called broker*. Nothing +reconciles them. + +## Open questions + +- Should assigning a module that declares a `broker` own-secret issue its account, rather than + leaving a minted value that cannot work? +- Or should the minting path refuse that name, so the missing act is named at once instead of + discovered by a crash loop? +- What else declares an own-secret whose name means something to another part of the mesh?