diff --git a/04-ISSUES/049-a-module-can-serve-tools-and-nothing-can-call-them/00-report.md b/04-ISSUES/049-a-module-can-serve-tools-and-nothing-can-call-them/00-report.md new file mode 100644 index 0000000..7bab926 --- /dev/null +++ b/04-ISSUES/049-a-module-can-serve-tools-and-nothing-can-call-them/00-report.md @@ -0,0 +1,72 @@ +--- +status: open +opened: 2026-09-14 +located-in: [] +fixed-by: +amended-design: +--- + +# 049 — A module can serve tools, and nothing is allowed to call them + +## Symptom + +A module serves tools over the broker. Asking it one, from inside that same module's own container, +using its own credential, is refused: + +``` +Error: Channel closed by server: 403 (ACCESS-REFUSED) with message +"ACCESS_REFUSED - User '-' doesn't have permissions to queue 'amq.gen-...'" +``` + +A module's broker account is scoped to what it declares it emits and consumes. A tool call is a +request and a reply, and the reply arrives on a temporary queue the caller creates — which that +scope does not cover, and should not: a module that only publishes events has no business declaring +queues. + +So the account is right and the request is reasonable, and there is no account that can make it. + +## Why this matters + +**A module's tools are its operator-facing surface, and nothing can reach it.** The catalogue serves +five: what this mesh holds, what a module is made of, what provides a given provision, what depends +on a module, and what is stale. Those are the questions the catalogue exists to answer, and today +the answer to "how does anyone ask one" is that they run a container joined to the broker's network +namespace and authenticate as the substrate's bootstrap admin. + +**It is why a running module gets mistaken for a working one.** A test that cannot ask a module +anything checks that its container is up, and a container being up is not a claim about function. +That substitution has now hidden two separate faults in one week — a crash-looping runtime beside a +healthy container of a similar name, and a catalogue that was never installed at all. + +**The mesh already has the shape for this.** An account scoped to a purpose, minted by the mesh and +sealed to a holder, is exactly what provisioning does. What is missing is the recognition that +*asking* is a use of a module, the way pulling is a use of the artifact store +([042](../042-nothing-gives-a-node-an-account-for-a-registry/00-report.md)) rather than something +that happens beneath it. + +## Evidence + +The refusal above, from a module invoking its own tool with the credential the mesh gave it. The +working alternative, which is the measure of the gap: + +``` +docker run --rm --network container: \ + -e MESH_BROKER_URL=amqp://@127.0.0.1:/ \ + invoke '{}' +``` + +That is the bootstrap credential, host-local, with no scope at all. It works, and nothing about it +should be how a mesh is asked a question. + +## Open questions + +- **Who is the caller?** An operator at a terminal, an agent acting for one, and another module are + three different holders with three different scopes, and only the last resembles anything the + mesh mints today. +- **Should calling be a grant like any other** — a module declares it may be asked, and a consumer + is issued an account that may create a reply queue and publish to that module's request queue? +- **Should the control plane be the way in?** It holds an admin connection already, and a + `mesh-control` subcommand for asking a module a question would need no new account — at the cost + of routing every question through one process and its privileges. +- **What does a module declare about being asked?** Serving a tool is already declared. Whether it + may be asked by anyone who can reach the broker, or only by named holders, is not.