Every module.json already declares a why for each port under listens (e.g. gitea: "why": "git over ssh. Not 22: the machine's own daemon holds that, and a module does not take it"). It's parsed (Listening.Why) but was only ever consumed for firewall-rule generation — confirmed by running mesh-controller plan novox against the live mesh: the output lists modules as assigned/needs/holds with zero port information anywhere. An operator deciding whether to assign a module had no way to see what it would open without reading the manifest by hand.
plan <node> now prints each assigned module's listens entries right under the module line:
minio assigned
listens 9000/tcp from mesh — the S3 endpoint, load-balanced across the 4-node erasure-coded cluster
listens 9001/tcp from mesh — the admin console, load-balanced across the 4-node erasure-coded cluster
A module with no listens, or a listens entry with no why, prints exactly as much as it has (no dash, no blank line).
Scope: only plan's text output. push has no preview/dry-run path of its own — it either sends or doesn't — so there's nothing else to update. plan --json is unchanged; it already prints the full declaration, listens included, for anything parsing it.
Verification:go build ./... and go vet ./... clean. go test ./... passes except the same two pre-existing failures already on main (gitea SSH port, resolver machines file — both in internal/catalogue, untouched by this change). plan itself talks to a real DB-backed inventory (openStores), so it can't be exercised end-to-end without a live store; the print logic is pulled into its own listensLines helper and unit-tested directly instead, matching how this package already tests small pure functions (e.g. mayIssue in modules_test.go) rather than full CLI invocations.
Branched off current main — separate from #55 (the ContributesMany work), not built on top of it.
Every `module.json` already declares a `why` for each port under `listens` (e.g. gitea: `"why": "git over ssh. Not 22: the machine's own daemon holds that, and a module does not take it"`). It's parsed (`Listening.Why`) but was only ever consumed for firewall-rule generation — confirmed by running `mesh-controller plan novox` against the live mesh: the output lists modules as `assigned`/`needs`/`holds` with zero port information anywhere. An operator deciding whether to assign a module had no way to see what it would open without reading the manifest by hand.
`plan <node>` now prints each assigned module's `listens` entries right under the module line:
```
minio assigned
listens 9000/tcp from mesh — the S3 endpoint, load-balanced across the 4-node erasure-coded cluster
listens 9001/tcp from mesh — the admin console, load-balanced across the 4-node erasure-coded cluster
```
A module with no `listens`, or a listens entry with no `why`, prints exactly as much as it has (no dash, no blank line).
**Scope:** only `plan`'s text output. `push` has no preview/dry-run path of its own — it either sends or doesn't — so there's nothing else to update. `plan --json` is unchanged; it already prints the full declaration, `listens` included, for anything parsing it.
**Verification:** `go build ./...` and `go vet ./...` clean. `go test ./...` passes except the same two pre-existing failures already on `main` (gitea SSH port, resolver machines file — both in `internal/catalogue`, untouched by this change). `plan` itself talks to a real DB-backed inventory (`openStores`), so it can't be exercised end-to-end without a live store; the print logic is pulled into its own `listensLines` helper and unit-tested directly instead, matching how this package already tests small pure functions (e.g. `mayIssue` in `modules_test.go`) rather than full CLI invocations.
Branched off current `main` — separate from #55 (the `ContributesMany` work), not built on top of it.
Every module.json already declares a why for each port under listens,
but plan only ever used it to build the firewall's rule set — nothing
printed it. An operator deciding whether to assign a module had no way
to see what it would open without reading the manifest by hand.
plan <node> now prints each assigned module's listens entries — port,
protocol, source, and its why — right under the module line, so the
same text that feeds the firewall is visible at the point someone is
actually deciding whether to open it.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Every
module.jsonalready declares awhyfor each port underlistens(e.g. gitea:"why": "git over ssh. Not 22: the machine's own daemon holds that, and a module does not take it"). It's parsed (Listening.Why) but was only ever consumed for firewall-rule generation — confirmed by runningmesh-controller plan novoxagainst the live mesh: the output lists modules asassigned/needs/holdswith zero port information anywhere. An operator deciding whether to assign a module had no way to see what it would open without reading the manifest by hand.plan <node>now prints each assigned module'slistensentries right under the module line:A module with no
listens, or a listens entry with nowhy, prints exactly as much as it has (no dash, no blank line).Scope: only
plan's text output.pushhas no preview/dry-run path of its own — it either sends or doesn't — so there's nothing else to update.plan --jsonis unchanged; it already prints the full declaration,listensincluded, for anything parsing it.Verification:
go build ./...andgo vet ./...clean.go test ./...passes except the same two pre-existing failures already onmain(gitea SSH port, resolver machines file — both ininternal/catalogue, untouched by this change).planitself talks to a real DB-backed inventory (openStores), so it can't be exercised end-to-end without a live store; the print logic is pulled into its ownlistensLineshelper and unit-tested directly instead, matching how this package already tests small pure functions (e.g.mayIssueinmodules_test.go) rather than full CLI invocations.Branched off current
main— separate from #55 (theContributesManywork), not built on top of it.